class RDF::Transaction

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`.

@example Executing a read-only transaction

repository = RDF::Repository.new

RDF::Transaction.begin(repository) do |tx|
  tx.query({predicate: RDF::Vocab::DOAP.developer}) do |statement|
    puts statement.inspect
  end
end

@example Executing a read/write transaction

repository = RDF::Repository.new

RDF::Transaction.begin(repository, mutable: true) do |tx|
  subject = RDF::URI("http://example.org/article")
  tx.delete [subject, RDF::RDFS.label, "Old title"]
  tx.insert [subject, RDF::RDFS.label, "New title"]
end

The base class provides an atomic write implementation depending on {RDF::Changeset} and using {Changeset#apply}. Custom {Repository} classes can implement a minimial write-atomic transactions by overriding {#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 {RDF::Dataset} returned by {#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 {#snapshot}.

@example A repository with a custom transaction class

class MyRepository < RDF::Repository
  DEFAULT_TX_CLASS = MyTransaction
  # ...
  # custom repository logic
  # ...
end

@see RDF::Changeset @see RDF::Mutable#apply_changeset @since 0.3.0