Class: RDF::Transaction

Inherits:
Object
  • Object
show all
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 ‘RDF::Changeset` and using `Changeset#apply`. Custom `Repositories` 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`.

Examples:

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

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

A repository with a custom transaction class

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

See Also:

Since:

  • 0.3.0

Defined Under Namespace

Classes: TransactionError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Queryable

#enum_for, #first, #first_literal, #first_object, #first_predicate, #first_subject, #first_value, #query

Methods included from Enumerable

#dump, #each_graph, #each_object, #each_predicate, #each_quad, #each_statement, #each_subject, #each_term, #each_triple, #enum_for, #enum_graph, #enum_object, #enum_predicate, #enum_quad, #enum_statement, #enum_subject, #enum_term, #enum_triple, #graph_names, #has_graph?, #has_object?, #has_predicate?, #has_quad?, #has_subject?, #has_term?, #has_triple?, #invalid?, #method_missing, #objects, #predicates, #project_graph, #quads, #respond_to_missing?, #statements, #subjects, #supports?, #terms, #to_a, #to_hash, #to_set, #triples, #valid?, #validate!

Methods included from Util::Aliasing::LateBound

#alias_method

Methods included from Countable

#count, #empty?, #enum_for

Methods included from Mutable

#<<, #apply_changeset, #clear, #delete, #delete_insert, #immutable?, #insert, #load, #method_missing, #respond_to_missing?, #snapshot, #update

Methods included from Writable

#<<, #insert

Constructor Details

#initialize(repository, graph_name: nil, mutable: false, **options) {|tx| ... } ⇒ Transaction

Initializes this transaction.

Parameters:

  • options (Hash{Symbol => Object})
  • mutable (Boolean) (defaults to: false)

    (false) Whether this is a read-only or read/write transaction.

Yields:

  • (tx)

Yield Parameters:

Raises:

Since:

  • 0.3.0



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, **options, &block)
  @repository = repository
  @snapshot = 
    repository.supports?(:snapshots) ? repository.snapshot : repository
  @options    = 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

#changesRDF::Changeset (readonly)

RDF statement mutations to apply when executed.

Returns:

Since:

  • 2.0.0



114
115
116
# File 'lib/rdf/transaction.rb', line 114

def changes
  @changes
end

#graph_nameRDF::Resource? (readonly)

The default graph name to apply to statements inserted or deleted by the transaction.

Returns:

Since:

  • 2.0.0



107
108
109
# File 'lib/rdf/transaction.rb', line 107

def graph_name
  @graph_name
end

#optionsHash{Symbol => Object} (readonly)

Any additional options for this transaction.

Returns:

  • (Hash{Symbol => Object})

Since:

  • 0.3.0



120
121
122
# File 'lib/rdf/transaction.rb', line 120

def options
  @options
end

#repositoryRDF::Repository (readonly)

The repository being operated upon.

Returns:

Since:

  • 2.0.0



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.

Parameters:

  • repository (RDF::Repository)
  • mutable (Boolean) (defaults to: false)

    (false) Whether this is a read-only or read/write transaction.

  • options (Hash{Symbol => Object})

Yields:

  • (tx)

Yield Parameters:

Since:

  • 0.3.0



90
91
92
# File 'lib/rdf/transaction.rb', line 90

def self.begin(repository, mutable: false, **options, &block)
  self.new(repository, options.merge(mutable: mutable), &block)
end

Instance Method Details

#each(*args, &block) ⇒ Object

See Also:

  • Enumerable#each

Since:

  • 0.3.0



76
77
78
# File 'lib/rdf/transaction.rb', line 76

def each(*args, &block)
  read_target.each(*args, &block)
end

#executeBoolean

Executes the transaction

Returns:

  • (Boolean)

    ‘true` if the changes are successfully applied.

Raises:

Since:

  • 0.3.0



215
216
217
218
219
# File 'lib/rdf/transaction.rb', line 215

def execute
  raise TransactionError, 'Cannot execute a rolled back transaction. ' \
                          'Open a new one instead.' if @rolledback
  @changes.apply(@repository)
end

#has_statement?(statement) ⇒ Boolean

Returns:

  • (Boolean)

See Also:

Since:

  • 0.3.0



188
189
190
# File 'lib/rdf/transaction.rb', line 188

def has_statement?(statement)
  read_target.has_statement?(statement)
end

#inspectString

Returns a developer-friendly representation of this transaction.

Returns:

  • (String)

Since:

  • 0.3.0



196
197
198
199
# File 'lib/rdf/transaction.rb', line 196

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

Since:

  • 0.3.0



206
207
208
# File 'lib/rdf/transaction.rb', line 206

def inspect!
  $stderr.puts(inspect)
end

#isolation_levelObject

See Also:

Since:

  • 0.3.0



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.

Returns:

  • (Boolean)

See Also:

  • Writable#mutable?

Since:

  • 0.3.0



173
174
175
# File 'lib/rdf/transaction.rb', line 173

def mutable?
  @mutable
end

#readable?Boolean

Returns ‘true` to indicate that this transaction is readable.

Returns:

  • (Boolean)

See Also:

Since:

  • 0.3.0



182
183
184
# File 'lib/rdf/transaction.rb', line 182

def readable?
  true
end

#rollbackBoolean

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.

Returns:

  • (Boolean)

    ‘true` if the changes are successfully applied.

Since:

  • 0.3.0



232
233
234
235
# File 'lib/rdf/transaction.rb', line 232

def rollback
  @changes = RDF::Changeset.new
  @rolledback = true
end

#writable?Boolean

Returns ‘true` if this is a read/write transaction, `false` otherwise.

Returns:

  • (Boolean)

See Also:

Since:

  • 0.3.0



164
165
166
# File 'lib/rdf/transaction.rb', line 164

def writable?
  @mutable
end