Class: RDF::Transaction
- Inherits:
-
Object
- Object
- RDF::Transaction
- Includes:
- Enumerable, Mutable, Queryable
- Defined in:
- lib/rdf/transaction.rb
Overview
An RDF transaction.
Transactions provide an ACID scope for queries and mutations.
Repository implementations may provide support for transactional updates by providing an atomic implementation of Mutable#apply_changeset and responding to ‘#supports?(:atomic_write)` with `true`.
We carefully distinguish between read-only and read/write transactions, in order to enable repository implementations to take out the appropriate locks for concurrency control. Transactions are read-only by default; mutability must be explicitly requested on construction in order to obtain a read/write transaction.
Individual repositories may make their own sets of guarantees within the transaction’s scope. In case repository implementations should be unable to provide full ACID guarantees for transactions, that must be clearly indicated in their documentation. If update atomicity is not provided, ‘#supports?(:atomic_write)` must respond `false`.
The base class provides an atomic write implementation depending on Changeset and using Changeset#apply. Custom Repository classes can implement a minimial write-atomic transactions by overriding Mutable#apply_changeset.
Reads within a transaction run against the live repository by default (#isolation_level is ‘:read_committed`). Repositories may provide support for snapshots by implementing Repository#snapshot and responding `true` to `#supports?(:snapshots)`. In this case, the transaction will use the Dataset returned by Mutable#snapshot for reads (`:repeatable_read`).
For datastores that support transactions natively, implementation of a custom Transaction subclass is recommended. The Repository is responsible for specifying snapshot support and isolation level as appropriate. Note that repositories may provide the snapshot isolation level without implementing Mutable#snapshot.
Direct Known Subclasses
Defined Under Namespace
Classes: SerializedTransaction, TransactionError
Instance Attribute Summary collapse
-
#changes ⇒ RDF::Changeset
readonly
RDF statement mutations to apply when executed.
-
#graph_name ⇒ RDF::Resource?
readonly
The default graph name to apply to statements inserted or deleted by the transaction.
-
#options ⇒ Hash{Symbol => Object}
readonly
Any additional options for this transaction.
-
#repository ⇒ RDF::Repository
readonly
The repository being operated upon.
Class Method Summary collapse
-
.begin(repository, mutable: false, **options) {|tx| ... } ⇒ void
Executes a transaction against the given RDF repository.
Instance Method Summary collapse
- #each(*args, &block) ⇒ Object
-
#execute ⇒ Boolean
Executes the transaction.
-
#initialize(repository, graph_name: nil, mutable: false, **options) {|tx| ... } ⇒ Transaction
constructor
Initializes this transaction.
-
#inspect ⇒ String
Returns a developer-friendly representation of this transaction.
-
#inspect! ⇒ void
Outputs a developer-friendly representation of this transaction to ‘stderr`.
- #isolation_level ⇒ Object
-
#mutable? ⇒ Boolean
Returns ‘true` if this is a read/write transaction, `false` otherwise.
-
#mutated? ⇒ Boolean
Indicates whether the transaction includes changes relative to the target repository’s state at transaction start time.
-
#readable? ⇒ Boolean
Returns ‘true` to indicate that this transaction is readable.
-
#rollback ⇒ Boolean
Rolls back the transaction.
- #statement?(*args) ⇒ Object (also: #has_statement?)
-
#writable? ⇒ Boolean
Returns ‘true` if this is a read/write transaction, `false` otherwise.
Methods included from Queryable
#enum_for, #first, #first_literal, #first_object, #first_predicate, #first_subject, #first_value, #query
Methods included from Enumerable
#canonicalize, #canonicalize!, #dump, #each_graph, #each_object, #each_predicate, #each_quad, #each_statement, #each_subject, #each_term, #each_triple, #enum_graph, #enum_object, #enum_predicate, #enum_quad, #enum_statement, #enum_subject, #enum_term, #enum_triple, #graph?, #graph_names, #invalid?, #object?, #objects, #predicate?, #predicates, #project_graph, #quad?, #quads, #statements, #subject?, #subjects, #supports?, #term?, #terms, #to_a, #to_h, #to_set, #triple?, #triples, #valid?, #validate!
Methods included from Util::Aliasing::LateBound
Methods included from Countable
Methods included from Mutable
#<<, #apply_changeset, #clear, #delete, #delete_insert, #immutable?, #insert, #load, #method_missing, #respond_to_missing?, #snapshot, #update
Methods included from Writable
Constructor Details
#initialize(repository, graph_name: nil, mutable: false, **options) {|tx| ... } ⇒ Transaction
Initializes this transaction.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/rdf/transaction.rb', line 130 def initialize(repository, graph_name: nil, mutable: false, **, &block) @repository = repository @snapshot = repository.supports?(:snapshots) ? repository.snapshot : repository @options = .dup @mutable = mutable @graph_name = graph_name raise TransactionError, 'Tried to open a mutable transaction on an immutable repository' if @mutable && !@repository.mutable? @changes = RDF::Changeset.new if block_given? case block.arity when 1 then block.call(self) else self.instance_eval(&block) end end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class RDF::Enumerable
Instance Attribute Details
#changes ⇒ RDF::Changeset (readonly)
RDF statement mutations to apply when executed.
114 115 116 |
# File 'lib/rdf/transaction.rb', line 114 def changes @changes end |
#graph_name ⇒ RDF::Resource? (readonly)
The default graph name to apply to statements inserted or deleted by the transaction.
107 108 109 |
# File 'lib/rdf/transaction.rb', line 107 def graph_name @graph_name end |
#options ⇒ Hash{Symbol => Object} (readonly)
Any additional options for this transaction.
120 121 122 |
# File 'lib/rdf/transaction.rb', line 120 def @options end |
#repository ⇒ RDF::Repository (readonly)
The repository being operated upon.
99 100 101 |
# File 'lib/rdf/transaction.rb', line 99 def repository @repository end |
Class Method Details
.begin(repository, mutable: false, **options) {|tx| ... } ⇒ void
This method returns an undefined value.
Executes a transaction against the given RDF repository.
90 91 92 |
# File 'lib/rdf/transaction.rb', line 90 def self.begin(repository, mutable: false, **, &block) self.new(repository, mutable: mutable, **, &block) end |
Instance Method Details
#each(*args, &block) ⇒ Object
76 77 78 |
# File 'lib/rdf/transaction.rb', line 76 def each(*args, &block) read_target.each(*args, &block) end |
#execute ⇒ Boolean
Executes the transaction
248 249 250 251 252 |
# File 'lib/rdf/transaction.rb', line 248 def execute raise TransactionError, 'Cannot execute a rolled back transaction. ' \ 'Open a new one instead.' if instance_variable_defined?(:@rolledback) && @rolledback @changes.apply(@repository) end |
#inspect ⇒ String
Returns a developer-friendly representation of this transaction.
229 230 231 232 |
# File 'lib/rdf/transaction.rb', line 229 def inspect sprintf("#<%s:%#0x(changes: -%d/+%d)>", self.class.name, self.__id__, self.changes.deletes.count, self.changes.inserts.count) end |
#inspect! ⇒ void
This method returns an undefined value.
Outputs a developer-friendly representation of this transaction to ‘stderr`.
239 240 241 |
# File 'lib/rdf/transaction.rb', line 239 def inspect! $stderr.puts(inspect) end |
#isolation_level ⇒ Object
154 155 156 157 |
# File 'lib/rdf/transaction.rb', line 154 def isolation_level return :repeatable_read if repository.supports?(:snapshots) :read_committed end |
#mutable? ⇒ Boolean
Returns ‘true` if this is a read/write transaction, `false` otherwise.
196 197 198 |
# File 'lib/rdf/transaction.rb', line 196 def mutable? @mutable end |
#mutated? ⇒ Boolean
‘Transaction` implementers may choose to `NotImplementedError` if the transaction implementation cannot be implemented efficiently.
Indicates whether the transaction includes changes relative to the target repository’s state at transaction start time.
The response is guaranteed to be ‘true` if executing the transaction against the original repository state would cause a change. It may also return `true` in cases where the repository would not change (e.g. because the transaction would insert statements already present).
175 176 177 178 179 180 |
# File 'lib/rdf/transaction.rb', line 175 def mutated? return !changes.empty? if self.class == Transaction raise NotImplementedError, '#mutated? is not implemented for #{self.class}' end |
#readable? ⇒ Boolean
Returns ‘true` to indicate that this transaction is readable.
205 206 207 |
# File 'lib/rdf/transaction.rb', line 205 def readable? true end |
#rollback ⇒ Boolean
Rolls back the transaction
@note: the base class simply replaces its current ‘Changeset` with a
fresh one. Other implementations may need to explictly rollback
at the supporting datastore.
@note: clients should not rely on using same transaction instance after
rollback.
265 266 267 268 |
# File 'lib/rdf/transaction.rb', line 265 def rollback @changes = RDF::Changeset.new @rolledback = true end |
#statement? ⇒ Boolean #statement?(statement) ⇒ Object Also known as: has_statement?
216 217 218 219 220 221 222 |
# File 'lib/rdf/transaction.rb', line 216 def statement?(*args) case args.length when 0 then false when 1 then read_target.statement?(*args) else raise ArgumentError("wrong number of arguments (given #{args.length}, expected 0 or 1)") end end |
#writable? ⇒ Boolean
Returns ‘true` if this is a read/write transaction, `false` otherwise.
187 188 189 |
# File 'lib/rdf/transaction.rb', line 187 def writable? @mutable end |