grumlin 0.21.0 → 0.21.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.lock +6 -6
- data/README.md +24 -0
- data/lib/grumlin/action.rb +12 -7
- data/lib/grumlin/client.rb +56 -18
- data/lib/grumlin/config.rb +26 -0
- data/lib/grumlin/features/feature_list.rb +19 -0
- data/lib/grumlin/features/neptune_features.rb +13 -0
- data/lib/grumlin/features/tinkergraph_features.rb +13 -0
- data/lib/grumlin/features.rb +16 -0
- data/lib/grumlin/repository/instance_methods.rb +1 -1
- data/lib/grumlin/request_dispatcher.rb +4 -43
- data/lib/grumlin/request_error_factory.rb +59 -0
- data/lib/grumlin/steppable.rb +8 -3
- data/lib/grumlin/transaction.rb +51 -0
- data/lib/grumlin/traversal_start.rb +9 -0
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +11 -11
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95963424bb38728bb21c2d3746a93be60daf303b2c2897b24d99684fd1fb7bff
|
4
|
+
data.tar.gz: 809b4b589b2bc5fd8687f0f63a6f73f17aab82060990b3a5942164c2ac92ef83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b095f3958ebf4b70577e607a0b10e27fc073055fe08d6fe55787c71fd104e77cf78cdcd0a54d06e7881c9183cab479ef6e92e6117a1c1b418945d1ebb417655c
|
7
|
+
data.tar.gz: 0477a8ce8ccfc5ea1cc3b95b7b48501b05387207918478adc4eecb8e286e8495910b6bd718e1d5c00ab63e61e02ca6e89212913ec35d8df49b267cb75281e9eb
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.21.
|
4
|
+
grumlin (0.21.1)
|
5
5
|
async-pool (~> 0.3)
|
6
6
|
async-websocket (~> 0.19)
|
7
7
|
oj (~> 3.13)
|
@@ -21,11 +21,11 @@ GEM
|
|
21
21
|
console (~> 1.10)
|
22
22
|
nio4r (~> 2.3)
|
23
23
|
timers (~> 4.1)
|
24
|
-
async-http (0.
|
24
|
+
async-http (0.57.0)
|
25
25
|
async (>= 1.25)
|
26
26
|
async-io (>= 1.28)
|
27
27
|
async-pool (>= 0.2)
|
28
|
-
protocol-http (~> 0.
|
28
|
+
protocol-http (~> 0.23.1)
|
29
29
|
protocol-http1 (~> 0.14.0)
|
30
30
|
protocol-http2 (~> 0.14.0)
|
31
31
|
traces (~> 0.4.0)
|
@@ -37,7 +37,7 @@ GEM
|
|
37
37
|
rspec (~> 3.0)
|
38
38
|
rspec-files (~> 1.0)
|
39
39
|
rspec-memory (~> 1.0)
|
40
|
-
async-websocket (0.19.
|
40
|
+
async-websocket (0.19.2)
|
41
41
|
async-http (~> 0.54)
|
42
42
|
async-io (~> 1.23)
|
43
43
|
protocol-websocket (~> 0.7.0)
|
@@ -72,7 +72,7 @@ GEM
|
|
72
72
|
racc (~> 1.4)
|
73
73
|
nokogiri (1.13.6-x86_64-linux)
|
74
74
|
racc (~> 1.4)
|
75
|
-
oj (3.13.
|
75
|
+
oj (3.13.20)
|
76
76
|
overcommit (0.59.1)
|
77
77
|
childprocess (>= 0.6.3, < 5)
|
78
78
|
iniparse (~> 1.4)
|
@@ -81,7 +81,7 @@ GEM
|
|
81
81
|
parser (3.1.2.0)
|
82
82
|
ast (~> 2.4.1)
|
83
83
|
protocol-hpack (1.4.2)
|
84
|
-
protocol-http (0.
|
84
|
+
protocol-http (0.23.1)
|
85
85
|
protocol-http1 (0.14.4)
|
86
86
|
protocol-http (~> 0.22)
|
87
87
|
protocol-http2 (0.14.2)
|
data/README.md
CHANGED
@@ -46,9 +46,33 @@ Or install it yourself as:
|
|
46
46
|
```ruby
|
47
47
|
Grumlin.configure do |config|
|
48
48
|
config.url = "ws://localhost:8182/gremlin"
|
49
|
+
|
50
|
+
# make sure you select right provider for better compatibility
|
51
|
+
config.provider = :tinkergraph
|
49
52
|
end
|
50
53
|
```
|
51
54
|
|
55
|
+
#### Providers
|
56
|
+
|
57
|
+
Currently `Grumlin` supports 2 providers:
|
58
|
+
- tinkergraph (default)
|
59
|
+
- neptune
|
60
|
+
|
61
|
+
As different providers may have or may have not support for specific features it's recommended to
|
62
|
+
explicitly specify the provider you use.
|
63
|
+
|
64
|
+
#### Provider features
|
65
|
+
|
66
|
+
Every provider is described by a set of features. In the future `Grumlin` may decide to disable or enable
|
67
|
+
some parts of it's functionality to comply provider's supported features. Currently there is no difference
|
68
|
+
in behaviour when working with different providers.
|
69
|
+
|
70
|
+
To check current providers supported features use
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Grumlin.features
|
74
|
+
```
|
75
|
+
|
52
76
|
### Traversing graphs
|
53
77
|
|
54
78
|
**Warning**: Not all steps and expressions defined in the reference documentation are supported.
|
data/lib/grumlin/action.rb
CHANGED
@@ -4,14 +4,15 @@ module Grumlin
|
|
4
4
|
class Action < Steppable
|
5
5
|
attr_reader :name, :args, :params, :next_step, :configuration_steps, :previous_step, :shortcut
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# client is only used when a traversal is a part of transaction
|
8
|
+
def initialize(name, args: [], params: {}, previous_step: nil, pool: Grumlin.default_pool, session_id: nil)
|
9
|
+
super(pool: pool, session_id: session_id)
|
10
|
+
|
9
11
|
@name = name.to_sym
|
10
12
|
@args = args # TODO: add recursive validation: only json types or Action
|
11
13
|
@params = params # TODO: add recursive validation: only json types
|
12
14
|
@previous_step = previous_step
|
13
15
|
@shortcut = shortcuts[@name]
|
14
|
-
@pool = pool || Grumlin.default_pool
|
15
16
|
end
|
16
17
|
|
17
18
|
def configuration_step?
|
@@ -73,14 +74,18 @@ module Grumlin
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def toList
|
76
|
-
|
77
|
-
client.write(bytecode)
|
78
|
-
end
|
77
|
+
client_write(bytecode)
|
79
78
|
end
|
80
79
|
|
81
80
|
def iterate
|
81
|
+
client_write(bytecode(no_return: true))
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def client_write(payload)
|
82
87
|
@pool.acquire do |client|
|
83
|
-
client.write(
|
88
|
+
client.write(payload, session_id: @session_id)
|
84
89
|
end
|
85
90
|
end
|
86
91
|
end
|
data/lib/grumlin/client.rb
CHANGED
@@ -24,8 +24,14 @@ module Grumlin
|
|
24
24
|
@client.close
|
25
25
|
end
|
26
26
|
|
27
|
-
def write(bytecode)
|
28
|
-
@client.write(bytecode)
|
27
|
+
def write(bytecode, session_id: nil)
|
28
|
+
@client.write(bytecode, session_id: session_id)
|
29
|
+
ensure
|
30
|
+
@count += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def finalize_tx(action, session_id)
|
34
|
+
@client.finalize_tx(action, session_id)
|
29
35
|
ensure
|
30
36
|
@count += 1
|
31
37
|
end
|
@@ -94,19 +100,19 @@ module Grumlin
|
|
94
100
|
end
|
95
101
|
|
96
102
|
# TODO: support yielding
|
97
|
-
def write(bytecode)
|
103
|
+
def write(bytecode, session_id: nil)
|
98
104
|
raise NotConnectedError unless connected?
|
99
105
|
|
100
|
-
request = to_query(bytecode)
|
101
|
-
|
102
|
-
|
106
|
+
request = to_query(bytecode, session_id: session_id)
|
107
|
+
submit_request(request)
|
108
|
+
end
|
103
109
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
+
def finalize_tx(action, session_id)
|
111
|
+
raise NotConnectedError unless connected?
|
112
|
+
raise ArgumentError, "session_id cannot be nil" if session_id.nil?
|
113
|
+
|
114
|
+
request = finalize_tx_query(action, session_id)
|
115
|
+
submit_request(request)
|
110
116
|
end
|
111
117
|
|
112
118
|
def inspect
|
@@ -119,20 +125,52 @@ module Grumlin
|
|
119
125
|
|
120
126
|
private
|
121
127
|
|
128
|
+
def submit_request(request)
|
129
|
+
channel = @request_dispatcher.add_request(request)
|
130
|
+
@transport.write(request)
|
131
|
+
|
132
|
+
begin
|
133
|
+
channel.dequeue.flat_map { |item| Typing.cast(item) }
|
134
|
+
rescue Async::Stop, Async::TimeoutError
|
135
|
+
close(check_requests: false)
|
136
|
+
raise
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
122
140
|
# This might be overridden in successors
|
123
141
|
def build_transport
|
124
142
|
Transport.new(@url, parent: @parent, **@client_options)
|
125
143
|
end
|
126
144
|
|
127
|
-
def to_query(bytecode)
|
145
|
+
def to_query(bytecode, session_id:)
|
146
|
+
{
|
147
|
+
requestId: SecureRandom.uuid,
|
148
|
+
op: :bytecode,
|
149
|
+
processor: session_id ? :session : :traversal,
|
150
|
+
args: {
|
151
|
+
gremlin: {
|
152
|
+
:@type => "g:Bytecode",
|
153
|
+
:@value => bytecode.serialize
|
154
|
+
},
|
155
|
+
aliases: { g: :g },
|
156
|
+
session: session_id
|
157
|
+
}.compact
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
def finalize_tx_query(action, session_id)
|
128
162
|
{
|
129
163
|
requestId: SecureRandom.uuid,
|
130
|
-
op:
|
131
|
-
processor:
|
164
|
+
op: :bytecode,
|
165
|
+
processor: session_id ? :session : :traversal,
|
132
166
|
args: {
|
133
|
-
gremlin: {
|
134
|
-
|
135
|
-
|
167
|
+
gremlin: {
|
168
|
+
:@type => "g:Bytecode",
|
169
|
+
:@value => { source: [[:tx, action]] }
|
170
|
+
},
|
171
|
+
aliases: { g: :g },
|
172
|
+
session: session_id
|
173
|
+
}.compact
|
136
174
|
}
|
137
175
|
end
|
138
176
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class Config
|
5
|
+
attr_accessor :url, :pool_size, :client_concurrency, :client_factory, :provider
|
6
|
+
|
7
|
+
SUPPORTED_PROVIDERS = %i[neptune tinkergraph].freeze
|
8
|
+
|
9
|
+
class ConfigurationError < Grumlin::Error; end
|
10
|
+
|
11
|
+
class UnknownProviderError < ConfigurationError; end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@pool_size = 10
|
15
|
+
@client_concurrency = 5
|
16
|
+
@provider = :tinkergraph
|
17
|
+
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate!
|
21
|
+
return if SUPPORTED_PROVIDERS.include?(provider.to_sym)
|
22
|
+
|
23
|
+
raise UnknownProviderError, "provider '#{provider}' is unknown. Supported providers: #{SUPPORTED_PROVIDERS}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Features
|
5
|
+
class FeatureList
|
6
|
+
def user_supplied_ids?
|
7
|
+
raise(NotImplementedError) if @user_supplied_ids.nil?
|
8
|
+
|
9
|
+
@user_supplied_ids
|
10
|
+
end
|
11
|
+
|
12
|
+
def supports_transactions?
|
13
|
+
raise(NotImplementedError) if @supports_transactions.nil?
|
14
|
+
|
15
|
+
@supports_transactions
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Features
|
5
|
+
class << self
|
6
|
+
FEATURES = {
|
7
|
+
neptune: NeptuneFeatures.new,
|
8
|
+
tinkergraph: TinkergraphFeatures.new
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
def for(provider)
|
12
|
+
FEATURES[provider]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -8,7 +8,7 @@ module Grumlin
|
|
8
8
|
extend Forwardable
|
9
9
|
|
10
10
|
UPSERT_RETRY_PARAMS = {
|
11
|
-
on: [Grumlin::AlreadyExistsError, Grumlin::
|
11
|
+
on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentModificationError],
|
12
12
|
sleep_method: ->(n) { Async::Task.current.sleep(n) },
|
13
13
|
tries: 3,
|
14
14
|
sleep: ->(n) { (n**2) + 1 + rand }
|
@@ -10,23 +10,6 @@ module Grumlin
|
|
10
10
|
206 => :partial_content
|
11
11
|
}.freeze
|
12
12
|
|
13
|
-
ERRORS = {
|
14
|
-
499 => InvalidRequestArgumentsError,
|
15
|
-
500 => ServerError,
|
16
|
-
597 => ScriptEvaluationError,
|
17
|
-
599 => ServerSerializationError,
|
18
|
-
598 => ServerTimeoutError,
|
19
|
-
|
20
|
-
401 => ClientSideError,
|
21
|
-
407 => ClientSideError,
|
22
|
-
498 => ClientSideError
|
23
|
-
}.freeze
|
24
|
-
|
25
|
-
VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
|
26
|
-
EDGE_ALREADY_EXISTS = "Edge with id already exists:"
|
27
|
-
CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
|
28
|
-
CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
|
29
|
-
|
30
13
|
class DispatcherError < Grumlin::Error; end
|
31
14
|
|
32
15
|
class RequestAlreadyAddedError < DispatcherError; end
|
@@ -47,14 +30,16 @@ module Grumlin
|
|
47
30
|
|
48
31
|
# builds a response object, when it's ready sends it to the client via a channel
|
49
32
|
# TODO: sometimes response does not include requestID, no idea how to handle it so far.
|
50
|
-
def add_response(response) # rubocop:disable Metrics/AbcSize
|
33
|
+
def add_response(response) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
51
34
|
request_id = response[:requestId]
|
52
35
|
raise UnknownRequestError unless ongoing_request?(request_id)
|
53
36
|
|
54
37
|
begin
|
55
38
|
request = @requests[request_id]
|
56
39
|
|
57
|
-
|
40
|
+
RequestErrorFactory.build(request, response).tap do |err|
|
41
|
+
raise err unless err.nil?
|
42
|
+
end
|
58
43
|
|
59
44
|
case SUCCESS[response.dig(:status, :code)]
|
60
45
|
when :success
|
@@ -91,29 +76,5 @@ module Grumlin
|
|
91
76
|
request = @requests.delete(request_id)
|
92
77
|
request[:channel].close
|
93
78
|
end
|
94
|
-
|
95
|
-
def check_errors!(status, query)
|
96
|
-
if (error = ERRORS[status[:code]])
|
97
|
-
raise (
|
98
|
-
already_exists_error(status) ||
|
99
|
-
concurrent_insert_error(status) ||
|
100
|
-
error
|
101
|
-
).new(status, query)
|
102
|
-
end
|
103
|
-
|
104
|
-
return unless SUCCESS[status[:code]].nil?
|
105
|
-
|
106
|
-
raise(UnknownResponseStatus, status)
|
107
|
-
end
|
108
|
-
|
109
|
-
def already_exists_error(status)
|
110
|
-
return VertexAlreadyExistsError if status[:message]&.include?(VERTEX_ALREADY_EXISTS)
|
111
|
-
return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
|
112
|
-
end
|
113
|
-
|
114
|
-
def concurrent_insert_error(status)
|
115
|
-
return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
|
116
|
-
return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
|
117
|
-
end
|
118
79
|
end
|
119
80
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class RequestErrorFactory
|
5
|
+
ERRORS = {
|
6
|
+
499 => InvalidRequestArgumentsError,
|
7
|
+
500 => ServerError,
|
8
|
+
597 => ScriptEvaluationError,
|
9
|
+
599 => ServerSerializationError,
|
10
|
+
598 => ServerTimeoutError,
|
11
|
+
|
12
|
+
401 => ClientSideError,
|
13
|
+
407 => ClientSideError,
|
14
|
+
498 => ClientSideError
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
# Neptune presumably returns message as a JSON string of format
|
18
|
+
# {"detailedMessage":"",
|
19
|
+
# "requestId":"UUID",
|
20
|
+
# "code":"ConcurrentModificationException"}
|
21
|
+
# Currencly we simply search for substings to identify the exact error
|
22
|
+
# TODO: parse json and use `code` instead
|
23
|
+
VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
|
24
|
+
EDGE_ALREADY_EXISTS = "Edge with id already exists:"
|
25
|
+
CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
|
26
|
+
CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
|
27
|
+
CONCURRENCT_MODIFICATION_FAILED = "Failed to complete operation due to conflicting concurrent"
|
28
|
+
|
29
|
+
class << self
|
30
|
+
def build(request, response)
|
31
|
+
status = response[:status]
|
32
|
+
query = request[:request]
|
33
|
+
|
34
|
+
if (error = ERRORS[status[:code]])
|
35
|
+
return (
|
36
|
+
already_exists_error(status) ||
|
37
|
+
concurrent_modification_error(status) ||
|
38
|
+
error
|
39
|
+
).new(status, query)
|
40
|
+
end
|
41
|
+
|
42
|
+
return unless RequestDispatcher::SUCCESS[status[:code]].nil?
|
43
|
+
|
44
|
+
UnknownResponseStatus.new(status)
|
45
|
+
end
|
46
|
+
|
47
|
+
def already_exists_error(status)
|
48
|
+
return VertexAlreadyExistsError if status[:message]&.include?(VERTEX_ALREADY_EXISTS)
|
49
|
+
return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
|
50
|
+
end
|
51
|
+
|
52
|
+
def concurrent_modification_error(status)
|
53
|
+
return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
|
54
|
+
return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
|
55
|
+
return ConcurrentModificationError if status[:message]&.include?(CONCURRENCT_MODIFICATION_FAILED)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/grumlin/steppable.rb
CHANGED
@@ -4,13 +4,18 @@ module Grumlin
|
|
4
4
|
class Steppable
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
+
attr_reader :session_id
|
8
|
+
|
7
9
|
START_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
|
8
10
|
REGULAR_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
|
9
11
|
CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
|
10
12
|
|
11
13
|
ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
|
12
14
|
|
13
|
-
def initialize
|
15
|
+
def initialize(pool: Grumlin.default_pool, session_id: nil)
|
16
|
+
@pool = pool
|
17
|
+
@session_id = session_id
|
18
|
+
|
14
19
|
return if respond_to?(:shortcuts)
|
15
20
|
|
16
21
|
raise "steppable must not be initialized directly, use Grumlin::Shortcuts::Storage#g or #__ instead"
|
@@ -18,12 +23,12 @@ module Grumlin
|
|
18
23
|
|
19
24
|
ALL_STEPS.each do |step|
|
20
25
|
define_method step do |*args, **params|
|
21
|
-
shortcuts.action_class.new(step, args: args, params: params, previous_step: self)
|
26
|
+
shortcuts.action_class.new(step, args: args, params: params, previous_step: self, session_id: @session_id)
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
25
30
|
def step(name, *args, **params)
|
26
|
-
shortcuts.action_class.new(name, args: args, params: params, previous_step: self)
|
31
|
+
shortcuts.action_class.new(name, args: args, params: params, previous_step: self, session_id: @session_id)
|
27
32
|
end
|
28
33
|
|
29
34
|
def_delegator :shortcuts, :__
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class Transaction
|
5
|
+
attr_reader :uuid
|
6
|
+
|
7
|
+
include Console
|
8
|
+
|
9
|
+
def initialize(traversal_start_class, pool: Grumlin.default_pool)
|
10
|
+
@traversal_start_class = traversal_start_class
|
11
|
+
@pool = pool
|
12
|
+
|
13
|
+
if supported?
|
14
|
+
@uuid = SecureRandom.uuid
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
logger.info(self) do
|
19
|
+
"#{Grumlin.config.provider} does not support transactions. commit and rollback are ignored, data will be saved"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def supported?
|
24
|
+
Grumlin.features.supports_transactions?
|
25
|
+
end
|
26
|
+
|
27
|
+
def begin
|
28
|
+
@traversal_start_class.new(session_id: @uuid)
|
29
|
+
end
|
30
|
+
|
31
|
+
def commit
|
32
|
+
return unless supported?
|
33
|
+
|
34
|
+
finalize(:commit)
|
35
|
+
end
|
36
|
+
|
37
|
+
def rollback
|
38
|
+
return unless supported?
|
39
|
+
|
40
|
+
finalize(:rollback)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def finalize(action)
|
46
|
+
@pool.acquire do |client|
|
47
|
+
client.finalize_tx(action, @uuid)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -4,6 +4,15 @@ module Grumlin
|
|
4
4
|
class TraversalStart < Steppable
|
5
5
|
include WithExtension
|
6
6
|
|
7
|
+
class TraversalError < Grumlin::Error; end
|
8
|
+
class AlreadyBoundToTransationError < TraversalError; end
|
9
|
+
|
10
|
+
def tx
|
11
|
+
raise AlreadyBoundToTransationError if @session_id
|
12
|
+
|
13
|
+
Transaction.new(self.class, pool: @pool)
|
14
|
+
end
|
15
|
+
|
7
16
|
def to_s(*)
|
8
17
|
self.class.to_s
|
9
18
|
end
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
@@ -98,7 +98,8 @@ module Grumlin
|
|
98
98
|
class VertexAlreadyExistsError < AlreadyExistsError; end
|
99
99
|
class EdgeAlreadyExistsError < AlreadyExistsError; end
|
100
100
|
|
101
|
-
class
|
101
|
+
class ConcurrentModificationError < ServerError; end
|
102
|
+
class ConcurrentInsertFailedError < ConcurrentModificationError; end
|
102
103
|
|
103
104
|
class ConcurrentVertexInsertFailedError < ConcurrentInsertFailedError; end
|
104
105
|
class ConcurrentEdgeInsertFailedError < ConcurrentInsertFailedError; end
|
@@ -127,27 +128,26 @@ module Grumlin
|
|
127
128
|
|
128
129
|
class WrongQueryResult < RepositoryError; end
|
129
130
|
|
130
|
-
class Config
|
131
|
-
attr_accessor :url, :pool_size, :client_concurrency, :client_factory
|
132
|
-
|
133
|
-
def initialize
|
134
|
-
@pool_size = 10
|
135
|
-
@client_concurrency = 5
|
136
|
-
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
131
|
@pool_mutex = Mutex.new
|
141
132
|
|
142
133
|
class << self
|
143
134
|
def configure
|
144
135
|
yield config
|
136
|
+
|
137
|
+
config.validate!
|
145
138
|
end
|
146
139
|
|
147
140
|
def config
|
148
141
|
@config ||= Config.new
|
149
142
|
end
|
150
143
|
|
144
|
+
# returns a subset of features for currently configured backend.
|
145
|
+
# The features lists are hardcoded as there is no way to get them
|
146
|
+
# from the remote server.
|
147
|
+
def features
|
148
|
+
Features.for(config.provider) # no memoization as provider may be changed
|
149
|
+
end
|
150
|
+
|
151
151
|
def default_pool
|
152
152
|
if Thread.current.thread_variable_get(:grumlin_default_pool)
|
153
153
|
return Thread.current.thread_variable_get(:grumlin_default_pool)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grumlin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.21.
|
4
|
+
version: 0.21.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/grumlin/action.rb
|
117
117
|
- lib/grumlin/benchmark/repository.rb
|
118
118
|
- lib/grumlin/client.rb
|
119
|
+
- lib/grumlin/config.rb
|
119
120
|
- lib/grumlin/edge.rb
|
120
121
|
- lib/grumlin/expressions/cardinality.rb
|
121
122
|
- lib/grumlin/expressions/column.rb
|
@@ -128,12 +129,17 @@ files:
|
|
128
129
|
- lib/grumlin/expressions/t.rb
|
129
130
|
- lib/grumlin/expressions/text_p.rb
|
130
131
|
- lib/grumlin/expressions/with_options.rb
|
132
|
+
- lib/grumlin/features.rb
|
133
|
+
- lib/grumlin/features/feature_list.rb
|
134
|
+
- lib/grumlin/features/neptune_features.rb
|
135
|
+
- lib/grumlin/features/tinkergraph_features.rb
|
131
136
|
- lib/grumlin/path.rb
|
132
137
|
- lib/grumlin/property.rb
|
133
138
|
- lib/grumlin/repository.rb
|
134
139
|
- lib/grumlin/repository/error_handling_strategy.rb
|
135
140
|
- lib/grumlin/repository/instance_methods.rb
|
136
141
|
- lib/grumlin/request_dispatcher.rb
|
142
|
+
- lib/grumlin/request_error_factory.rb
|
137
143
|
- lib/grumlin/shortcut.rb
|
138
144
|
- lib/grumlin/shortcuts.rb
|
139
145
|
- lib/grumlin/shortcuts/properties.rb
|
@@ -151,6 +157,7 @@ files:
|
|
151
157
|
- lib/grumlin/test/rspec.rb
|
152
158
|
- lib/grumlin/test/rspec/db_cleaner_context.rb
|
153
159
|
- lib/grumlin/test/rspec/gremlin_context.rb
|
160
|
+
- lib/grumlin/transaction.rb
|
154
161
|
- lib/grumlin/transport.rb
|
155
162
|
- lib/grumlin/traversal_start.rb
|
156
163
|
- lib/grumlin/traversal_strategies/options_strategy.rb
|