Skip to content

Commit

Permalink
store Transaction in Thread.current[:transaction], only one transacti…
Browse files Browse the repository at this point in the history
…on per thread is allowed in neo4j
  • Loading branch information
andreasronge committed Jun 3, 2008
1 parent 184ea76 commit 019f9d0
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 268 deletions.
23 changes: 12 additions & 11 deletions README.rdoc
Expand Up @@ -16,29 +16,29 @@ Example:

include 'neo'

Neo4j::transaction {
node = Neo4j::Node.new
Neo4j::Transaction.run {
node = MyNode.new
}

Example of how to do rollback

Neo4j::transaction { |t|
Neo4j::Transaction.run { |t|
# something failed
t.failure # will cause a rollback
}


If a block is not given then the transaction method will return a transaction object.
You can also run it without a block, like this:

transaction = Neo4j::transaction
transaction.begin
# etc ...
transaction = Neo4j::Transaction.new
transaction.start
# do something
transaction.finish

=== Neo::Node
A Neo::Node is a wrapper around the java org.neo4j.api.core.Node interface.
A node can have properties and relationships to other nodes.


Use the Neo::Node mixin

class MyNode
Expand Down Expand Up @@ -75,10 +75,11 @@ If you want you can also declare those properties, like the attr_accessor

And to set a property

Neo::transaction {
f = SomeNode.new
f.foo = "123"
}

Those declared properties will also be indexed, see below: Quering.


=== Relationships

Expand Down Expand Up @@ -183,7 +184,7 @@ It does this by reading the classname property and loading that ruby class with
f1 = Foo.new {}

# load the class again
f2 = Neo::neo_service.find_node(foo.neo_node_id)
f2 = Neo4j::Neo.instance.find_node(foo.neo_node_id)
f2.hello
# f2.class == Foo

1 change: 0 additions & 1 deletion lib/neo4j/lucene.rb
Expand Up @@ -66,7 +66,6 @@ def find(fields)
query = BooleanQuery.new

fields.each_pair do |key,value|
puts "search '#{key.to_s}' '#{value}'"
term = Term.new(key.to_s, value)
q = TermQuery.new(term)
query.add(q, BooleanClause::Occur::MUST)
Expand Down
4 changes: 2 additions & 2 deletions lib/neo4j/neo.rb
Expand Up @@ -6,7 +6,7 @@ module Neo4j
LUCENE_INDEX_STORAGE = 'var/lucene'

#
# Allows start and stop the Neo4j service
# Allows run and stop the Neo4j service
#
# A wrapper class around org.neo4j.api.core.EmbeddedNeo
#
Expand All @@ -31,7 +31,7 @@ def start(storage = NEO_STORAGE)
@lucene = Lucene.new LUCENE_INDEX_STORAGE

ref_node = nil
Neo4j::transaction do
Neo4j::Transaction.run do
ref_node = @neo.getReferenceNode
@meta_nodes = MetaNodes.new(ref_node)
end
Expand Down
8 changes: 5 additions & 3 deletions lib/neo4j/node.rb
Expand Up @@ -25,9 +25,11 @@ def initialize(*args)
# was a neo java node provided ?
if args.length == 1 and args[0].kind_of?(org.neo4j.api.core.Node)
init_with_node(args[0])
elsif block_given? # check if we should run in a transaction
Neo4j::transaction { init_without_node; yield self }
else # initialize without a new transaction
elsif block_given? and Neo4j::Transaction.running? # block but no new transaction ?
init_without_node; yield self
elsif block_given? and !Neo4j::Transaction.running? # check if we should run in a transaction
Neo4j::Transaction.run { init_without_node; yield self }
else
init_without_node
end

Expand Down
215 changes: 110 additions & 105 deletions lib/neo4j/transaction.rb
@@ -1,134 +1,139 @@

module Neo4j

#
# Runs a block in a Neo4j transaction
#
# Most operations on neo requires an transaction.
# include 'neo'
#
# Neo4j::transaction {
# node = Neo4j.new
# }
#
# You have also access to transaction object
#
# Neo4j::transaction { |t|
# # something failed
# t.failure # will cause a rollback
# }
#
#
# If a block is not given than the transaction method will return a transaction object.
#
# transaction = Neo4j::transaction
# transaction.begin
# etc ...
#
#
def transaction
return Neo4j::Transaction.new unless block_given?

tx = Neo4j::Transaction.new

tx.begin
ret = nil

begin
ret = yield tx
tx.success unless tx.failure?
rescue Exception => e
raise e
ensure
tx.finish
end
ret
end

module_function :transaction

#
# Wraps a Neo4j java transaction
# Wraps a Neo4j java transaction.
# There can only be one transaction per thread.
#
class Transaction
# holds the wrapped org.neo4j.api.core.Transaction
attr_accessor :internal
attr_reader :neo_transaction

@@counter = 0 # just for debugging purpose, not thread safe ...

@@instance = nil

#
# Runs a block in a Neo4j transaction
#
# Most operations on neo requires an transaction.
# include 'neo'
#
# Neo4j::Transaction.run {
# node = Neo4j.new
# }
#
# You have also access to transaction object
#
# Neo4j::Transaction.run { |t|
# # something failed
# t.failure # will cause a rollback
# }
#
#
# If a block is not given than the transaction method will return a transaction object.
#
# transaction = Neo4j::Transaction.run
#
def self.run
$NEO_LOGGER.warn{"already start transaction, tried to run start twice"} if Transaction.running?
raise ArgumentError.new("Expected a block to run in Transaction.run") unless block_given?


if !Transaction.running?
tx = Neo4j::Transaction.new
tx.start
else
tx = Transaction.current
end

ret = nil

begin
ret = yield tx
tx.success unless tx.failure?
rescue Exception => e
raise e
ensure
tx.finish
end
ret
end

def initialize
$NEO_LOGGER.debug{"create new transaction"}
raise Exception.new("Can't create a new transaction because one is already running (#{Transaction.current})") if Transaction.running?
@@counter += 1
Thread.current[:transaction] = self
$NEO_LOGGER.debug{"create #{self.to_s}"}
end

def to_s
"Transaction: #{@@counter} failure: #{failure?}, running #{Transaction.running?}, thread: #{Thread.current.to_s}"
end

def self.current
Thread.current[:transaction]
end

#
# Get the current transaction
#
def self.instance
@@instance
def self.running?
self.current != nil && self.current.neo_transaction != nil
end

def self.failure?
current.failure?
end

def failure?
@failure
@failure == true
end

#
# Starts a new transaction
#
def begin
@@instance = self
@internal = org.neo4j.api.core.Transaction.begin
$NEO_LOGGER.debug{"begin transaction #{self.to_s}"}
@failure = false
self
end
def start
@neo_transaction= org.neo4j.api.core.Transaction.begin
@failure = false

$NEO_LOGGER.debug{"started #{self.to_s}"}
self
end


def to_s
@internal
end
#
# Marks this transaction as successful, which means that it will be commited
# upon invocation of finish() unless failure() has or will be invoked before then.
#
def success
raise Exception.new("no transaction started, can't do success on it") unless @internal
$NEO_LOGGER.debug{"success transaction #{self.to_s}"}
@internal.success
end
#
# Marks this transaction as successful, which means that it will be commited
# upon invocation of finish() unless failure() has or will be invoked before then.
#
def success
raise Exception.new("no transaction started, can't do success on it") unless Transaction.running?
$NEO_LOGGER.debug{"success #{self.to_s}"}
@neo_transaction.success
end


#
# Commits or marks this transaction for rollback, depending on whether success() or failure() has been previously invoked.
#
def finish
raise Exception.new("no transaction started, can't do success on it") unless @internal
$NEO_LOGGER.debug{"finish transaction #{self.to_s}"}
@internal.finish
@@instance = nil
end
#
# Commits or marks this transaction for rollback, depending on whether success() or failure() has been previously invoked.
#
def finish
raise Exception.new("no transaction started, can't do success on it") unless Transaction.running?
@neo_transaction.finish
@neo_transaction=nil
Thread.current[:transaction] = nil
$NEO_LOGGER.debug{"finished #{self.to_s}"}
end

#
# Marks this transaction as failed, which means that it will inexplicably
# be rolled back upon invocation of finish().
#
def failure
raise Exception.new("no transaction started, can't do failure on it") unless @internal
$NEO_LOGGER.debug{"failure transaction #{self.to_s}"}
@internal.failure
@failure = true
end


#
# Marks this transaction as failed, which means that it will inexplicably
# be rolled back upon invocation of finish().
#
def failure
raise Exception.new("no transaction started, can't do failure on it") unless Transaction.running?
@neo_transaction.failure
@failure = true
$NEO_LOGGER.debug{"failure #{self.to_s}"}
end


end

# $NEO_LOGGER = Object.new
# def $NEO_LOGGER.debug
# puts yield
# end
#
# n = Neo4j::Transaction.new
# n.begin
#

end

3 changes: 2 additions & 1 deletion test/lucene_spec.rb
Expand Up @@ -12,7 +12,8 @@
describe "When running in one transaction" do
before(:all) do
start
@transaction = Neo4j::transaction.begin
@transaction = Neo4j::Transaction.new
@transaction.start
end

after(:all) do
Expand Down
11 changes: 9 additions & 2 deletions test/neo_spec.rb
Expand Up @@ -15,12 +15,16 @@
end

after(:all) do
remove_class_defs # so that we can define the same class again
Neo4j::Transaction.run {
remove_class_defs # so that we can define the same class again
}
stop
end

before(:each) do
@transaction = Neo4j::transaction.begin
@transaction = Neo4j::Transaction.new
@transaction.start
puts "Before. ------- #{Neo4j::Transaction.current}"
end

after(:each) do
Expand All @@ -36,11 +40,13 @@
describe Neo4j::Neo do

it "should not find a meta node of a class that does not exist" do
puts "1. ------- #{Neo4j::Transaction.current}"
n = Neo4j::Neo.instance.find_meta_node('Kalle2')
n.should be_nil
end

it "should find the meta node of a class that exists" do
puts "2. ------- #{Neo4j::Transaction.current}"
class Kalle2 < Neo4j::BaseNode
end

Expand All @@ -50,6 +56,7 @@ class Kalle2 < Neo4j::BaseNode
end

it "should find an (ruby) object stored in neo given its unique id" do
puts "3. ------- #{Neo4j::Transaction.current}"
class Foo45 < Neo4j::BaseNode
end

Expand Down

0 comments on commit 019f9d0

Please sign in to comment.