Skip to content

Commit

Permalink
Finish 3.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Dec 30, 2021
2 parents 9a7adc2 + 0f69e30 commit 1c7b189
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 3 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.0
3.2.1
2 changes: 1 addition & 1 deletion lib/rdf/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def self.extend_object(obj)
Hash.new)
obj.instance_variable_set(:@tx_class,
obj.options.delete(:transaction_class) ||
DEFAULT_TX_CLASS)
RDF::Transaction::SerializedTransaction)
super
end

Expand Down
76 changes: 75 additions & 1 deletion lib/rdf/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,81 @@ def read_target
end

public


##
# A transaction with full serializability.
#
# @todo refactor me!
# @see RDF::Transaction
class SerializedTransaction < Transaction
##
# @see Transaction#initialize
def initialize(*args, **options, &block)
super(*args, **options, &block)
@base_snapshot = @snapshot
end

##
# Inserts the statement to the transaction's working snapshot.
#
# @see Transaction#insert_statement
def insert_statement(statement)
@snapshot = @snapshot.class
.new(data: @snapshot.send(:insert_to,
@snapshot.send(:data),
process_statement(statement)))
end

##
# Deletes the statement from the transaction's working snapshot.
#
# @see Transaction#insert_statement
def delete_statement(statement)
@snapshot = @snapshot.class
.new(data: @snapshot.send(:delete_from,
@snapshot.send(:data),
process_statement(statement)))
end

##
# @see RDF::Dataset#isolation_level
def isolation_level
:serializable
end

##
# @note this is a simple object equality check.
#
# @see RDF::Transaction#mutated?
def mutated?
!@snapshot.send(:data).equal?(repository.send(:data))
end

##
# Replaces repository data with the transaction's snapshot in a safely
# serializable fashion.
#
# @note this transaction uses a pessimistic merge strategy which
# fails the transaction if any data has changed in the repository
# since transaction start time. However, the specific guarantee is
# softer: multiple concurrent conflicting transactions will not
# succeed. We may choose to implement a less pessimistic merge
# strategy as a non-breaking change.
#
# @raise [TransactionError] when the transaction can't be merged.
# @see Transaction#execute
def execute
raise TransactionError, 'Cannot execute a rolled back transaction. ' \
'Open a new one instead.' if instance_variable_defined?(:@rolledback) && @rolledback

raise TransactionError, 'Error merging transaction. Repository' \
'has changed during transaction time.' unless
repository.send(:data).equal? @base_snapshot.send(:data)

repository.send(:data=, @snapshot.send(:data))
end
end # SerializedTransaction

##
# An error class for transaction failures.
#
Expand Down
16 changes: 16 additions & 0 deletions spec/repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,20 @@
expect(subject.statements.first).to eq existing_statement
expect(subject.statements.first).to be_inferred
end

it "performs coherent list updates" do
# List updates require reading from the repository mid-transaction, which requires a SerializedTransaction
repo = RDF::Repository.new
lh = RDF::Node.new("o")
repo << RDF::Statement.new(RDF::URI('s'), RDF::URI('p'), lh)
expect(repo.count).to eq 1
repo.transaction(mutable: true) do |tx|
list = RDF::List.new(subject: lh, graph: tx, values: %w(a b c))
expect(tx.count).to eq 7
list[0, 2] = %(d)
expect(tx.count).to eq 5
end
expect(repo.count).to eq 5

end
end
7 changes: 7 additions & 0 deletions spec/transaction_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,10 @@ class MyTx < described_class; end
end
end
end

describe RDF::Transaction::SerializedTransaction do
let(:repository) { RDF::Repository.new }

# @see lib/rdf/spec/transaction.rb in rdf-spec
it_behaves_like "an RDF::Transaction", RDF::Transaction::SerializedTransaction
end

0 comments on commit 1c7b189

Please sign in to comment.