neo4j-core 6.1.6 → 7.0.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +4 -9
- data/README.md +48 -0
- data/lib/neo4j-core.rb +23 -0
- data/lib/neo4j-core/helpers.rb +8 -0
- data/lib/neo4j-core/query.rb +23 -20
- data/lib/neo4j-core/query_clauses.rb +18 -32
- data/lib/neo4j-core/query_find_in_batches.rb +3 -1
- data/lib/neo4j-core/version.rb +1 -1
- data/lib/neo4j-embedded/cypher_response.rb +4 -0
- data/lib/neo4j-embedded/embedded_database.rb +3 -5
- data/lib/neo4j-embedded/embedded_node.rb +4 -4
- data/lib/neo4j-embedded/embedded_session.rb +21 -10
- data/lib/neo4j-embedded/embedded_transaction.rb +4 -10
- data/lib/neo4j-server/cypher_node.rb +5 -4
- data/lib/neo4j-server/cypher_relationship.rb +3 -3
- data/lib/neo4j-server/cypher_response.rb +4 -0
- data/lib/neo4j-server/cypher_session.rb +31 -22
- data/lib/neo4j-server/cypher_transaction.rb +23 -15
- data/lib/neo4j-server/resource.rb +3 -4
- data/lib/neo4j/core/cypher_session.rb +17 -9
- data/lib/neo4j/core/cypher_session/adaptors.rb +116 -33
- data/lib/neo4j/core/cypher_session/adaptors/bolt.rb +331 -0
- data/lib/neo4j/core/cypher_session/adaptors/bolt/chunk_writer_io.rb +76 -0
- data/lib/neo4j/core/cypher_session/adaptors/bolt/pack_stream.rb +288 -0
- data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +60 -29
- data/lib/neo4j/core/cypher_session/adaptors/has_uri.rb +63 -0
- data/lib/neo4j/core/cypher_session/adaptors/http.rb +123 -119
- data/lib/neo4j/core/cypher_session/responses.rb +17 -2
- data/lib/neo4j/core/cypher_session/responses/bolt.rb +135 -0
- data/lib/neo4j/core/cypher_session/responses/embedded.rb +46 -11
- data/lib/neo4j/core/cypher_session/responses/http.rb +49 -40
- data/lib/neo4j/core/cypher_session/transactions.rb +33 -0
- data/lib/neo4j/core/cypher_session/transactions/bolt.rb +36 -0
- data/lib/neo4j/core/cypher_session/transactions/embedded.rb +32 -0
- data/lib/neo4j/core/cypher_session/transactions/http.rb +52 -0
- data/lib/neo4j/core/instrumentable.rb +2 -2
- data/lib/neo4j/core/label.rb +182 -0
- data/lib/neo4j/core/node.rb +8 -3
- data/lib/neo4j/core/relationship.rb +12 -4
- data/lib/neo4j/entity_equality.rb +1 -1
- data/lib/neo4j/session.rb +4 -5
- data/lib/neo4j/transaction.rb +108 -72
- data/neo4j-core.gemspec +6 -6
- metadata +34 -40
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'neo4j/core/cypher_session/transactions'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module Core
|
5
|
+
class CypherSession
|
6
|
+
module Transactions
|
7
|
+
class Embedded < Base
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
@java_tx = adaptor.graph_db.begin_tx
|
11
|
+
end
|
12
|
+
|
13
|
+
def commit
|
14
|
+
return if !@java_tx
|
15
|
+
|
16
|
+
@java_tx.success
|
17
|
+
@java_tx.close
|
18
|
+
rescue Java::OrgNeo4jGraphdb::TransactionFailureException => e
|
19
|
+
raise CypherError, e.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete
|
23
|
+
return if !@java_tx
|
24
|
+
|
25
|
+
@java_tx.failure
|
26
|
+
@java_tx.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'neo4j/core/cypher_session/transactions'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module Core
|
5
|
+
class CypherSession
|
6
|
+
module Transactions
|
7
|
+
class HTTP < Base
|
8
|
+
# Should perhaps have transaction adaptors only define #close
|
9
|
+
# commit/delete are, I think, an implementation detail
|
10
|
+
|
11
|
+
def commit
|
12
|
+
adaptor.requestor.request(:post, query_path(true)) if started?
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete
|
16
|
+
adaptor.requestor.request(:delete, query_path) if started?
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_path(commit = false)
|
20
|
+
if id
|
21
|
+
"/db/data/transaction/#{id}"
|
22
|
+
else
|
23
|
+
'/db/data/transaction'
|
24
|
+
end.tap do |path|
|
25
|
+
path << '/commit' if commit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Takes the transaction URL from Neo4j and parses out the ID
|
30
|
+
def apply_id_from_url!(url)
|
31
|
+
root.instance_variable_set('@id', url.match(%r{/(\d+)/?$})[1].to_i) if url
|
32
|
+
# @id = url.match(%r{/(\d+)/?$})[1].to_i if url
|
33
|
+
end
|
34
|
+
|
35
|
+
def started?
|
36
|
+
!!id
|
37
|
+
end
|
38
|
+
|
39
|
+
def id
|
40
|
+
root.instance_variable_get('@id')
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def connection
|
46
|
+
adaptor.connection
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -12,13 +12,13 @@ module Neo4j
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
-
def instrument(name, label, arguments
|
15
|
+
def instrument(name, label, arguments)
|
16
16
|
# defining class methods
|
17
17
|
klass = class << self; self; end
|
18
18
|
klass.instance_eval do
|
19
19
|
define_method("subscribe_to_#{name}") do |&b|
|
20
20
|
ActiveSupport::Notifications.subscribe(label) do |a, start, finish, id, payload|
|
21
|
-
b.call
|
21
|
+
b.call yield(a, start, finish, id, payload)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Core
|
3
|
+
class Label
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name, session)
|
7
|
+
@name = name
|
8
|
+
@session = session
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_index(property, options = {})
|
12
|
+
validate_index_options!(options)
|
13
|
+
properties = property.is_a?(Array) ? property.join(',') : property
|
14
|
+
schema_query("CREATE INDEX ON :`#{@name}`(#{properties})")
|
15
|
+
end
|
16
|
+
|
17
|
+
def drop_index(property, options = {})
|
18
|
+
validate_index_options!(options)
|
19
|
+
schema_query("DROP INDEX ON :`#{@name}`(#{property})")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a neo4j constraint on a property
|
23
|
+
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
24
|
+
# @example
|
25
|
+
# label = Neo4j::Label.create(:person, session)
|
26
|
+
# label.create_constraint(:name, {type: :unique}, session)
|
27
|
+
#
|
28
|
+
def create_constraint(property, constraints)
|
29
|
+
cypher = case constraints[:type]
|
30
|
+
when :unique, :uniqueness
|
31
|
+
"CREATE CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
32
|
+
else
|
33
|
+
fail "Not supported constraint #{constraints.inspect} for property #{property} (expected :type => :unique)"
|
34
|
+
end
|
35
|
+
schema_query(cypher)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_uniqueness_constraint(property, options = {})
|
39
|
+
create_constraint(property, options.merge(type: :unique))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Drops a neo4j constraint on a property
|
43
|
+
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
44
|
+
# @example
|
45
|
+
# label = Neo4j::Label.create(:person, session)
|
46
|
+
# label.create_constraint(:name, {type: :unique}, session)
|
47
|
+
# label.drop_constraint(:name, {type: :unique}, session)
|
48
|
+
#
|
49
|
+
def drop_constraint(property, constraint)
|
50
|
+
cypher = case constraint[:type]
|
51
|
+
when :unique, :uniqueness
|
52
|
+
"DROP CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
53
|
+
else
|
54
|
+
fail "Not supported constraint #{constraint.inspect}"
|
55
|
+
end
|
56
|
+
schema_query(cypher)
|
57
|
+
end
|
58
|
+
|
59
|
+
def drop_uniqueness_constraint(property, options = {})
|
60
|
+
drop_constraint(property, options.merge(type: :unique))
|
61
|
+
end
|
62
|
+
|
63
|
+
def indexes
|
64
|
+
@session.indexes.select do |definition|
|
65
|
+
definition[:label] == @name.to_sym
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.indexes_for(session)
|
70
|
+
session.indexes
|
71
|
+
end
|
72
|
+
|
73
|
+
def drop_indexes
|
74
|
+
indexes.each do |definition|
|
75
|
+
begin
|
76
|
+
@session.query("DROP INDEX ON :`#{definition[:label]}`(#{definition[:properties][0]})")
|
77
|
+
rescue Neo4j::Server::CypherResponse::ResponseError
|
78
|
+
# This will error on each constraint. Ignore and continue.
|
79
|
+
next
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.drop_indexes_for(session)
|
85
|
+
indexes_for(session).each do |definition|
|
86
|
+
begin
|
87
|
+
session.query("DROP INDEX ON :`#{definition[:label]}`(#{definition[:properties][0]})")
|
88
|
+
rescue Neo4j::Server::CypherResponse::ResponseError
|
89
|
+
# This will error on each constraint. Ignore and continue.
|
90
|
+
next
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def index?(property)
|
96
|
+
indexes.any? { |definition| definition[:properties] == [property.to_sym] }
|
97
|
+
end
|
98
|
+
|
99
|
+
def constraints(_options = {})
|
100
|
+
@session.constraints.select do |definition|
|
101
|
+
definition[:label] == @name.to_sym
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def uniqueness_constraints(_options = {})
|
106
|
+
constraints.select do |definition|
|
107
|
+
definition[:type] == :uniqueness
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def drop_uniqueness_constraints
|
112
|
+
uniqueness_constraints.each do |definition|
|
113
|
+
@session.query("DROP CONSTRAINT ON (n:`#{definition[:label]}`) ASSERT n.`#{definition[:properties][0]}` IS UNIQUE")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.drop_uniqueness_constraints_for(session)
|
118
|
+
session.constraints.each do |definition|
|
119
|
+
session.query("DROP CONSTRAINT ON (n:`#{definition[:label]}`) ASSERT n.`#{definition[:properties][0]}` IS UNIQUE")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def constraint?(property)
|
124
|
+
constraints.any? { |definition| definition[:properties] == [property.to_sym] }
|
125
|
+
end
|
126
|
+
|
127
|
+
def uniqueness_constraint?(property)
|
128
|
+
uniqueness_constraints.include?([property])
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.wait_for_schema_changes(session)
|
132
|
+
schema_threads(session).map(&:join)
|
133
|
+
set_schema_threads(session, [])
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# Store schema threads on the session so that we can easily wait for all
|
139
|
+
# threads on a session regardless of label
|
140
|
+
def schema_threads
|
141
|
+
self.class.schema_threads(@session)
|
142
|
+
end
|
143
|
+
|
144
|
+
def schema_threads=(array)
|
145
|
+
self.class.set_schema_threads(@session, array)
|
146
|
+
end
|
147
|
+
|
148
|
+
class << self
|
149
|
+
def schema_threads(session)
|
150
|
+
session.instance_variable_get('@_schema_threads') || []
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_schema_threads(session, array)
|
154
|
+
session.instance_variable_set('@_schema_threads', array)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Schema queries can run separately from other queries, but they should
|
159
|
+
# be mutually exclusive to each other or we get locking errors
|
160
|
+
# SCHEMA_QUERY_SEMAPHORE = Mutex.new
|
161
|
+
|
162
|
+
# If there is a transaction going on, this could block
|
163
|
+
# So we run in a thread and it will go through at the next opportunity
|
164
|
+
def schema_query(cypher)
|
165
|
+
# Thread.new do
|
166
|
+
# SCHEMA_QUERY_SEMAPHORE.synchronize do
|
167
|
+
|
168
|
+
@session.transaction { |tx| tx.query(cypher, {}) }
|
169
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
170
|
+
puts 'ERROR during schema query:', e.message, e.backtrace
|
171
|
+
|
172
|
+
# end
|
173
|
+
# end.tap { |thread| schema_threads << thread }
|
174
|
+
end
|
175
|
+
|
176
|
+
def validate_index_options!(options)
|
177
|
+
return unless options[:type] && options[:type] != :exact
|
178
|
+
fail "Type #{options[:type]} is not supported"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/lib/neo4j/core/node.rb
CHANGED
@@ -1,20 +1,25 @@
|
|
1
1
|
require 'neo4j/core/wrappable'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
2
3
|
|
3
4
|
module Neo4j
|
4
5
|
module Core
|
5
6
|
class Node
|
6
7
|
attr_reader :id, :labels, :properties
|
7
|
-
|
8
|
+
alias props properties
|
8
9
|
|
9
10
|
include Wrappable
|
10
11
|
|
11
12
|
# Perhaps we should deprecate this?
|
12
|
-
|
13
|
+
alias neo_id id
|
13
14
|
|
14
15
|
def initialize(id, labels, properties = {})
|
15
16
|
@id = id
|
16
17
|
@labels = labels.map(&:to_sym) unless labels.nil?
|
17
|
-
@properties = properties
|
18
|
+
@properties = properties.symbolize_keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
other.is_a?(Node) && neo_id == other.neo_id
|
18
23
|
end
|
19
24
|
|
20
25
|
class << self
|
@@ -1,14 +1,22 @@
|
|
1
|
+
require 'neo4j/core/wrappable'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
|
1
4
|
module Neo4j
|
2
5
|
module Core
|
3
6
|
class Relationship
|
4
|
-
attr_reader :id, :type, :properties
|
7
|
+
attr_reader :id, :type, :properties, :start_node_id, :end_node_id
|
8
|
+
alias props properties
|
9
|
+
alias neo_id id
|
10
|
+
alias rel_type type
|
5
11
|
|
6
12
|
include Wrappable
|
7
13
|
|
8
|
-
def initialize(id, type, properties)
|
14
|
+
def initialize(id, type, properties, start_node_id = nil, end_node_id = nil)
|
9
15
|
@id = id
|
10
16
|
@type = type.to_sym unless type.nil?
|
11
|
-
@properties = properties
|
17
|
+
@properties = properties.symbolize_keys
|
18
|
+
@start_node_id = start_node_id
|
19
|
+
@end_node_id = end_node_id
|
12
20
|
end
|
13
21
|
|
14
22
|
class << self
|
@@ -17,7 +25,7 @@ module Neo4j
|
|
17
25
|
type = nil # unknown
|
18
26
|
properties = properties
|
19
27
|
|
20
|
-
new(id, type, properties)
|
28
|
+
new(id, type, properties, nil, nil)
|
21
29
|
end
|
22
30
|
end
|
23
31
|
end
|
data/lib/neo4j/session.rb
CHANGED
@@ -36,11 +36,6 @@ module Neo4j
|
|
36
36
|
true # TODO
|
37
37
|
end
|
38
38
|
|
39
|
-
# @abstract
|
40
|
-
def begin_tx
|
41
|
-
fail 'not impl.'
|
42
|
-
end
|
43
|
-
|
44
39
|
class CypherError < StandardError
|
45
40
|
attr_reader :error_msg, :error_status, :error_code
|
46
41
|
def initialize(error_msg, error_code, error_status)
|
@@ -77,6 +72,10 @@ module Neo4j
|
|
77
72
|
fail 'not implemented'
|
78
73
|
end
|
79
74
|
|
75
|
+
def transaction_class
|
76
|
+
self.class.transaction_class
|
77
|
+
end
|
78
|
+
|
80
79
|
class << self
|
81
80
|
# Creates a new session to Neo4j.
|
82
81
|
# This will be the default session to be used unless there is already a session created (see #current and #set_current)
|
data/lib/neo4j/transaction.rb
CHANGED
@@ -1,39 +1,86 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
require 'active_support/per_thread_registry'
|
3
|
+
|
1
4
|
module Neo4j
|
2
5
|
module Transaction
|
3
6
|
extend self
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
# Provides a simple API to manage transactions for each session in a thread-safe manner
|
9
|
+
class TransactionsRegistry
|
10
|
+
extend ActiveSupport::PerThreadRegistry
|
11
|
+
|
12
|
+
attr_accessor :transactions_by_session_id
|
13
|
+
end
|
14
|
+
|
15
|
+
class Base
|
16
|
+
attr_reader :session, :root
|
17
|
+
|
18
|
+
def initialize(session, _options = {})
|
19
|
+
@session = session
|
20
|
+
|
21
|
+
Transaction.stack_for(session) << self
|
22
|
+
|
23
|
+
@root = Transaction.stack_for(session).first
|
24
|
+
# Neo4j::Core::Label::SCHEMA_QUERY_SEMAPHORE.lock if root?
|
25
|
+
|
26
|
+
# @parent = session_transaction_stack.last
|
27
|
+
# session_transaction_stack << self
|
10
28
|
end
|
11
29
|
|
12
|
-
|
13
|
-
|
14
|
-
|
30
|
+
def inspect
|
31
|
+
status_string = [:id, :failed?, :active?, :commit_url].map do |method|
|
32
|
+
"#{method}: #{send(method)}" if respond_to?(method)
|
33
|
+
end.compact.join(', ')
|
34
|
+
|
35
|
+
"<#{self.class} [#{status_string}]"
|
15
36
|
end
|
16
|
-
alias_method :failure, :mark_failed
|
17
37
|
|
18
|
-
#
|
19
|
-
def
|
20
|
-
|
38
|
+
# Commits or marks this transaction for rollback, depending on whether #mark_failed has been previously invoked.
|
39
|
+
def close
|
40
|
+
tx_stack = Transaction.stack_for(@session)
|
41
|
+
fail 'Tried closing when transaction stack is empty (maybe you closed too many?)' if tx_stack.empty?
|
42
|
+
fail "Closed transaction which wasn't the most recent on the stack (maybe you forgot to close one?)" if tx_stack.pop != self
|
43
|
+
|
44
|
+
@closed = true
|
45
|
+
|
46
|
+
post_close! if tx_stack.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete
|
50
|
+
fail 'not implemented'
|
51
|
+
end
|
52
|
+
|
53
|
+
def commit
|
54
|
+
fail 'not implemented'
|
21
55
|
end
|
22
|
-
alias_method :failure?, :failed?
|
23
56
|
|
24
57
|
def autoclosed!
|
25
58
|
@autoclosed = true if transient_failures_autoclose?
|
26
59
|
end
|
27
60
|
|
28
|
-
def
|
29
|
-
|
61
|
+
def closed?
|
62
|
+
!!@closed
|
30
63
|
end
|
31
64
|
|
32
|
-
|
33
|
-
|
65
|
+
# Marks this transaction as failed,
|
66
|
+
# which means that it will unconditionally be rolled back
|
67
|
+
# when #close is called.
|
68
|
+
# Aliased for legacy purposes.
|
69
|
+
def mark_failed
|
70
|
+
root.mark_failed if root && root != self
|
71
|
+
@failure = true
|
34
72
|
end
|
73
|
+
alias failure mark_failed
|
74
|
+
|
75
|
+
# If it has been marked as failed.
|
76
|
+
# Aliased for legacy purposes.
|
77
|
+
def failed?
|
78
|
+
!!@failure
|
79
|
+
end
|
80
|
+
alias failure? failed?
|
35
81
|
|
36
82
|
def mark_expired
|
83
|
+
@parent.mark_expired if @parent
|
37
84
|
@expired = true
|
38
85
|
end
|
39
86
|
|
@@ -41,43 +88,24 @@ module Neo4j
|
|
41
88
|
!!@expired
|
42
89
|
end
|
43
90
|
|
44
|
-
|
45
|
-
|
46
|
-
@pushed_nested += 1
|
91
|
+
def root?
|
92
|
+
@root == self
|
47
93
|
end
|
48
94
|
|
49
|
-
|
50
|
-
def pop_nested!
|
51
|
-
@pushed_nested -= 1
|
52
|
-
end
|
95
|
+
private
|
53
96
|
|
54
|
-
|
55
|
-
|
56
|
-
# See neo4j java docs.
|
57
|
-
# @param [Neo4j::Node,Neo4j::Relationship] entity
|
58
|
-
# @return [Java::OrgNeo4jKernelImplCoreapi::PropertyContainerLocker]
|
59
|
-
def acquire_read_lock(entity)
|
97
|
+
def transient_failures_autoclose?
|
98
|
+
Gem::Version.new(@session.version) >= Gem::Version.new('2.2.6')
|
60
99
|
end
|
61
100
|
|
62
|
-
|
63
|
-
|
64
|
-
# See neo4j java docs.
|
65
|
-
# @param [Neo4j::Node,Neo4j::Relationship] entity
|
66
|
-
# @return [Java::OrgNeo4jKernelImplCoreapi::PropertyContainerLocker]
|
67
|
-
def acquire_write_lock(entity)
|
101
|
+
def autoclosed?
|
102
|
+
!!@autoclosed
|
68
103
|
end
|
69
104
|
|
70
|
-
|
71
|
-
|
72
|
-
pop_nested!
|
73
|
-
return if @pushed_nested >= 0
|
74
|
-
fail "Can't commit transaction, already committed" if @pushed_nested < -1
|
75
|
-
Neo4j::Transaction.unregister(self)
|
76
|
-
post_close!
|
105
|
+
def active?
|
106
|
+
!closed?
|
77
107
|
end
|
78
108
|
|
79
|
-
private
|
80
|
-
|
81
109
|
def post_close!
|
82
110
|
return if autoclosed?
|
83
111
|
if failed?
|
@@ -89,56 +117,64 @@ module Neo4j
|
|
89
117
|
end
|
90
118
|
|
91
119
|
# @return [Neo4j::Transaction::Instance]
|
92
|
-
def new(
|
93
|
-
|
120
|
+
def new(session = Session.current!)
|
121
|
+
session.transaction
|
94
122
|
end
|
95
123
|
|
96
124
|
# Runs the given block in a new transaction.
|
97
125
|
# @param [Boolean] run_in_tx if true a new transaction will not be created, instead if will simply yield to the given block
|
98
126
|
# @@yield [Neo4j::Transaction::Instance]
|
99
|
-
def run(
|
127
|
+
def run(*args)
|
128
|
+
session, run_in_tx = session_and_run_in_tx_from_args(args)
|
129
|
+
|
100
130
|
fail ArgumentError, 'Expected a block to run in Transaction.run' unless block_given?
|
101
131
|
|
102
132
|
return yield(nil) unless run_in_tx
|
103
133
|
|
104
|
-
tx = Neo4j::Transaction.new
|
134
|
+
tx = Neo4j::Transaction.new(session)
|
105
135
|
yield tx
|
106
136
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
107
|
-
print_exception_cause(e)
|
137
|
+
# print_exception_cause(e)
|
138
|
+
|
108
139
|
tx.mark_failed unless tx.nil?
|
109
|
-
raise
|
140
|
+
raise e
|
110
141
|
ensure
|
111
142
|
tx.close unless tx.nil?
|
112
143
|
end
|
113
144
|
|
114
|
-
#
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
145
|
+
# To support old syntax of providing run_in_tx first
|
146
|
+
# But session first is ideal
|
147
|
+
def session_and_run_in_tx_from_args(args)
|
148
|
+
fail ArgumentError, 'Too many arguments' if args.size > 2
|
149
|
+
|
150
|
+
if args.empty?
|
151
|
+
[Session.current!, true]
|
152
|
+
else
|
153
|
+
result = args.dup
|
154
|
+
if result.size == 1
|
155
|
+
result << ([true, false].include?(args[0]) ? Session.current! : true)
|
156
|
+
end
|
122
157
|
|
123
|
-
|
124
|
-
|
158
|
+
[true, false].include?(result[0]) ? result.reverse : result
|
159
|
+
end
|
125
160
|
end
|
126
161
|
|
127
|
-
|
128
|
-
|
129
|
-
Thread.current[:neo4j_curr_tx] = nil if tx == Thread.current[:neo4j_curr_tx]
|
162
|
+
def current_for(session)
|
163
|
+
stack_for(session).first
|
130
164
|
end
|
131
165
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
fail 'Already running a transaction' if current
|
136
|
-
Thread.current[:neo4j_curr_tx] = tx
|
166
|
+
def stack_for(session)
|
167
|
+
TransactionsRegistry.transactions_by_session_id ||= {}
|
168
|
+
TransactionsRegistry.transactions_by_session_id[session.object_id] ||= []
|
137
169
|
end
|
138
170
|
|
139
|
-
|
140
|
-
|
141
|
-
|
171
|
+
private
|
172
|
+
|
173
|
+
def print_exception_cause(exception)
|
174
|
+
return if !exception.respond_to?(:cause) || !exception.cause.respond_to?(:print_stack_trace)
|
175
|
+
|
176
|
+
Core.logger.info "Java Exception in a transaction, cause: #{exception.cause}"
|
177
|
+
exception.cause.print_stack_trace
|
142
178
|
end
|
143
179
|
end
|
144
180
|
end
|