grumlin 0.21.1 → 0.22.2
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 +5 -5
- data/README.md +41 -10
- data/lib/grumlin/action.rb +1 -1
- data/lib/grumlin/client.rb +8 -42
- data/lib/grumlin/dummy_transaction.rb +25 -0
- data/lib/grumlin/repository/instance_methods.rb +41 -18
- data/lib/grumlin/request_error_factory.rb +16 -2
- data/lib/grumlin/steps.rb +3 -1
- data/lib/grumlin/steps_serializers/bytecode.rb +8 -6
- data/lib/grumlin/transaction.rb +9 -21
- data/lib/grumlin/traversal_start.rb +21 -3
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +6 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23a0ce8dacaf7c4add38674353e6aa69e51ebd9865ed83c3112f62ad96a5d213
|
4
|
+
data.tar.gz: 775c7c6009e6969d9a2b060772dc5d6caf9846267050509a5aeccf1bd5050e0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b9a820912679a67ca1be3545130ee3affcf89bea1bd9085a47f18d4b0c2ed6583ebfe5e7c52c339d93efe672d7ec7d5e604a68681581a4c57f6b21d8dce9e21
|
7
|
+
data.tar.gz: a5d371995de61123f954361143f98427af0109831c189bb1ad60cdf722109c72749872478096aa60de22bc7d3139825fdf1ed07c8cce2330eca51cb5865fc6f9
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.
|
4
|
+
grumlin (0.22.2)
|
5
5
|
async-pool (~> 0.3)
|
6
6
|
async-websocket (~> 0.19)
|
7
7
|
oj (~> 3.13)
|
@@ -21,14 +21,14 @@ GEM
|
|
21
21
|
console (~> 1.10)
|
22
22
|
nio4r (~> 2.3)
|
23
23
|
timers (~> 4.1)
|
24
|
-
async-http (0.
|
24
|
+
async-http (0.58.0)
|
25
25
|
async (>= 1.25)
|
26
26
|
async-io (>= 1.28)
|
27
27
|
async-pool (>= 0.2)
|
28
28
|
protocol-http (~> 0.23.1)
|
29
29
|
protocol-http1 (~> 0.14.0)
|
30
30
|
protocol-http2 (~> 0.14.0)
|
31
|
-
traces (
|
31
|
+
traces (>= 0.4.0)
|
32
32
|
async-io (1.33.0)
|
33
33
|
async
|
34
34
|
async-pool (0.3.10)
|
@@ -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.23.
|
84
|
+
protocol-http (0.23.4)
|
85
85
|
protocol-http1 (0.14.4)
|
86
86
|
protocol-http (~> 0.22)
|
87
87
|
protocol-http2 (0.14.2)
|
@@ -157,7 +157,7 @@ GEM
|
|
157
157
|
thor (1.2.1)
|
158
158
|
tilt (2.0.10)
|
159
159
|
timers (4.3.3)
|
160
|
-
traces (0.
|
160
|
+
traces (0.6.0)
|
161
161
|
tzinfo (2.0.4)
|
162
162
|
concurrent-ruby (~> 1.0)
|
163
163
|
unicode-display_width (2.1.0)
|
data/README.md
CHANGED
@@ -64,8 +64,7 @@ explicitly specify the provider you use.
|
|
64
64
|
#### Provider features
|
65
65
|
|
66
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.
|
68
|
-
in behaviour when working with different providers.
|
67
|
+
some parts of it's functionality to comply provider's supported features.
|
69
68
|
|
70
69
|
To check current providers supported features use
|
71
70
|
|
@@ -73,6 +72,12 @@ To check current providers supported features use
|
|
73
72
|
Grumlin.features
|
74
73
|
```
|
75
74
|
|
75
|
+
Current differences between providers:
|
76
|
+
|
77
|
+
| Feature | TinkerGraph |AWS Neptune|
|
78
|
+
|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
|
79
|
+
| Transactions | Transaction semantic is ignoroed, data is always writen, `tx.rollback` does nothing, an info is printed every time transactions are used with TinkerGraph |Full support
|
80
|
+
|
76
81
|
### Traversing graphs
|
77
82
|
|
78
83
|
**Warning**: Not all steps and expressions defined in the reference documentation are supported.
|
@@ -240,21 +245,29 @@ Each `return_mode` is mapped to a particular termination step:
|
|
240
245
|
- `:traversal` - do not execute the query and return the traversal as is
|
241
246
|
|
242
247
|
`Grumlin::Repository` also provides a set of generic CRUD operations:
|
243
|
-
- `add_vertex(label, id = nil, **properties)`
|
244
|
-
- `add_edge(label, id = nil, from:, to:, **properties)`
|
245
|
-
- `drop_vertex(id)`
|
246
|
-
- `drop_edge(id = nil, from: nil, to: nil, label: nil)`
|
248
|
+
- `add_vertex(label, id = nil, start: g, **properties)`
|
249
|
+
- `add_edge(label, id = nil, from:, to:, start: g, **properties)`
|
250
|
+
- `drop_vertex(id, start: g)`
|
251
|
+
- `drop_edge(id = nil, from: nil, to: nil, label: nil, start: g)`
|
252
|
+
- `drop_in_batches(traversal, batch_size: 10_000)`
|
247
253
|
|
248
254
|
and a few methods that emulate upserts:
|
249
|
-
- `upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, **params)`
|
250
|
-
- `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, **params)`
|
251
|
-
- `upsert_edges(edges, batch_size: 100, on_failure: :retry, **params)`
|
252
|
-
- `upsert_vertices(edges, batch_size: 100, on_failure: :retry, **params)`
|
255
|
+
- `upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
|
256
|
+
- `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
|
257
|
+
- `upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
|
258
|
+
- `upsert_vertices(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
|
253
259
|
|
254
260
|
All of them support 3 different modes for error handling: `:retry`, `:ignore` and `:raise`. Retry mode is implemented
|
255
261
|
with [retryable](https://github.com/nfedyashev/retryable). **params will be merged to the default config for upserts
|
256
262
|
and passed to `Retryable.retryable`. In case if you want to modify retryable behaviour you are to do so.
|
257
263
|
|
264
|
+
If you want to use these methods inside a transaction simply pass your `gtx` as `start` parameter:
|
265
|
+
```ruby
|
266
|
+
g.tx do |gtx|
|
267
|
+
add_vertex(:vertex, start: gtx)
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
258
271
|
If you don't want to define you own repository, simply use
|
259
272
|
|
260
273
|
`Grumlin::Repository.new` returns an instance of an anonymous class extending `Grumlin::Repository`.
|
@@ -282,6 +295,24 @@ it may be useful for debugging. Note that one needs to call a termination step m
|
|
282
295
|
|
283
296
|
method will return profiling data of the results.
|
284
297
|
|
298
|
+
#### Transactions
|
299
|
+
|
300
|
+
Since 0.22.0 `Grumlin` supports transactions when working with providers that supports them:
|
301
|
+
```ruby
|
302
|
+
# Using Transaction directly
|
303
|
+
tx = g.tx
|
304
|
+
gtx = tx.begin
|
305
|
+
gtx.addV(:vertex).iterate
|
306
|
+
tx.commit # or tx.rollback
|
307
|
+
|
308
|
+
# Using with a block
|
309
|
+
g.tx do |gtx|
|
310
|
+
gtx.addV(:vertex).iterate
|
311
|
+
# raise Grumlin::Rollback to manually rollback
|
312
|
+
# any other exception will also rollback the transaction and will be reraised
|
313
|
+
end # commits automatically
|
314
|
+
```
|
315
|
+
|
285
316
|
#### IRB
|
286
317
|
|
287
318
|
An example of how to start an IRB session with support for executing gremlin queries:
|
data/lib/grumlin/action.rb
CHANGED
data/lib/grumlin/client.rb
CHANGED
@@ -30,12 +30,6 @@ module Grumlin
|
|
30
30
|
@count += 1
|
31
31
|
end
|
32
32
|
|
33
|
-
def finalize_tx(action, session_id)
|
34
|
-
@client.finalize_tx(action, session_id)
|
35
|
-
ensure
|
36
|
-
@count += 1
|
37
|
-
end
|
38
|
-
|
39
33
|
def viable?
|
40
34
|
!closed?
|
41
35
|
end
|
@@ -104,15 +98,15 @@ module Grumlin
|
|
104
98
|
raise NotConnectedError unless connected?
|
105
99
|
|
106
100
|
request = to_query(bytecode, session_id: session_id)
|
107
|
-
submit_request(request)
|
108
|
-
end
|
109
101
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
102
|
+
channel = @request_dispatcher.add_request(request)
|
103
|
+
begin
|
104
|
+
@transport.write(request)
|
105
|
+
channel.dequeue.flat_map { |item| Typing.cast(item) }
|
106
|
+
rescue Async::Stop, Async::TimeoutError
|
107
|
+
close(check_requests: false)
|
108
|
+
raise
|
109
|
+
end
|
116
110
|
end
|
117
111
|
|
118
112
|
def inspect
|
@@ -125,18 +119,6 @@ module Grumlin
|
|
125
119
|
|
126
120
|
private
|
127
121
|
|
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
|
-
|
140
122
|
# This might be overridden in successors
|
141
123
|
def build_transport
|
142
124
|
Transport.new(@url, parent: @parent, **@client_options)
|
@@ -157,21 +139,5 @@ module Grumlin
|
|
157
139
|
}.compact
|
158
140
|
}
|
159
141
|
end
|
160
|
-
|
161
|
-
def finalize_tx_query(action, session_id)
|
162
|
-
{
|
163
|
-
requestId: SecureRandom.uuid,
|
164
|
-
op: :bytecode,
|
165
|
-
processor: session_id ? :session : :traversal,
|
166
|
-
args: {
|
167
|
-
gremlin: {
|
168
|
-
:@type => "g:Bytecode",
|
169
|
-
:@value => { source: [[:tx, action]] }
|
170
|
-
},
|
171
|
-
aliases: { g: :g },
|
172
|
-
session: session_id
|
173
|
-
}.compact
|
174
|
-
}
|
175
|
-
end
|
176
142
|
end
|
177
143
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class DummyTransaction < Transaction
|
5
|
+
attr_reader :uuid
|
6
|
+
|
7
|
+
include Console
|
8
|
+
|
9
|
+
def initialize(traversal_start_class, pool: Grumlin.default_pool) # rubocop:disable Lint/MissingSuper, Lint/UnusedMethodArgument
|
10
|
+
@traversal_start_class = traversal_start_class
|
11
|
+
|
12
|
+
logger.info(self) do
|
13
|
+
"#{Grumlin.config.provider} does not support transactions. commit and rollback are ignored, data will be saved"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def commit
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def rollback
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
module Repository
|
5
|
-
module InstanceMethods
|
5
|
+
module InstanceMethods # rubocop:disable Metrics/ModuleLength
|
6
6
|
include Grumlin::Expressions
|
7
7
|
|
8
8
|
extend Forwardable
|
@@ -22,51 +22,73 @@ module Grumlin
|
|
22
22
|
self.class.shortcuts
|
23
23
|
end
|
24
24
|
|
25
|
-
def drop_vertex(id)
|
26
|
-
|
25
|
+
def drop_vertex(id, start: g)
|
26
|
+
start.V(id).drop.iterate
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def drop_in_batches(traversal, batch_size: 10_000) # rubocop:disable Metrics/AbcSize
|
30
|
+
total_count = traversal.count.next
|
31
|
+
|
32
|
+
batches = (total_count / batch_size) + 1
|
33
|
+
|
34
|
+
Console.logger.info(self) do
|
35
|
+
"drop_in_batches: total_count: #{total_count}, batch_size: #{batch_size}, batches: #{batches}"
|
36
|
+
end
|
37
|
+
|
38
|
+
batches.times do |batch|
|
39
|
+
Console.logger.info(self) { "drop_in_batches: deleting batch #{batch + 1}/#{batches}..." }
|
40
|
+
traversal.limit(batch_size).drop.iterate
|
41
|
+
Console.logger.info(self) { "drop_in_batches: batch #{batch + 1}/#{batches} deleted" }
|
42
|
+
end
|
43
|
+
|
44
|
+
return if traversal.count.next.zero?
|
45
|
+
|
46
|
+
drop_in_batches(traversal, batch_size: batch_size)
|
47
|
+
|
48
|
+
Console.logger.info(self) { "drop_in_batches: finished." }
|
49
|
+
end
|
50
|
+
|
51
|
+
def drop_edge(id = nil, from: nil, to: nil, label: nil, start: g) # rubocop:disable Metrics/AbcSize
|
30
52
|
raise ArgumentError, "either id or from:, to: and label: must be passed" if [id, from, to, label].all?(&:nil?)
|
31
|
-
return
|
53
|
+
return start.E(id).drop.iterate unless id.nil?
|
32
54
|
|
33
55
|
raise ArgumentError, "from:, to: and label: must be passed" if [from, to, label].any?(&:nil?)
|
34
56
|
|
35
|
-
|
57
|
+
start.V(from).outE(label).where(__.inV.hasId(to)).limit(1).drop.iterate
|
36
58
|
end
|
37
59
|
|
38
|
-
def add_vertex(label, id = nil, **properties)
|
60
|
+
def add_vertex(label, id = nil, start: g, **properties)
|
39
61
|
id ||= properties[T.id]
|
40
62
|
properties = except(properties, T.id)
|
41
63
|
|
42
|
-
t =
|
64
|
+
t = start.addV(label)
|
43
65
|
t = t.props(T.id => id) unless id.nil?
|
44
66
|
t.props(**properties).next
|
45
67
|
end
|
46
68
|
|
47
|
-
def add_edge(label, id = nil, from:, to:, **properties)
|
69
|
+
def add_edge(label, id = nil, from:, to:, start: g, **properties)
|
48
70
|
id ||= properties[T.id]
|
49
71
|
properties = except(properties, T.label)
|
50
72
|
properties[T.id] = id
|
51
73
|
|
52
|
-
|
74
|
+
start.addE(label).from(__.V(from)).to(__.V(to)).props(**properties).next
|
53
75
|
end
|
54
76
|
|
55
|
-
def upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, **params)
|
77
|
+
def upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params) # rubocop:disable Metrics/ParameterLists
|
56
78
|
with_upsert_error_handling(on_failure, params) do
|
57
79
|
create_properties, update_properties = cleanup_properties(create_properties, update_properties)
|
58
80
|
|
59
|
-
|
81
|
+
start.upsertV(label, id, create_properties, update_properties).id.next
|
60
82
|
end
|
61
83
|
end
|
62
84
|
|
63
85
|
# vertices:
|
64
86
|
# [["label", "id", {create: :properties}, {update: properties}]]
|
65
87
|
# params can override Retryable config from UPSERT_RETRY_PARAMS
|
66
|
-
def upsert_vertices(vertices, batch_size: 100, on_failure: :retry, **params)
|
88
|
+
def upsert_vertices(vertices, batch_size: 100, on_failure: :retry, start: g, **params)
|
67
89
|
vertices.each_slice(batch_size) do |slice|
|
68
90
|
with_upsert_error_handling(on_failure, params) do
|
69
|
-
slice.reduce(
|
91
|
+
slice.reduce(start) do |t, (label, id, create_properties, update_properties)|
|
70
92
|
create_properties, update_properties = cleanup_properties(create_properties, update_properties)
|
71
93
|
|
72
94
|
t.upsertV(label, id, create_properties, update_properties)
|
@@ -77,20 +99,21 @@ module Grumlin
|
|
77
99
|
|
78
100
|
# Only from and to are used to find the existing edge, if one wants to assign an id to a created edge,
|
79
101
|
# it must be passed as T.id in create_properties.
|
80
|
-
def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {},
|
102
|
+
def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, # rubocop:disable Metrics/ParameterLists
|
103
|
+
on_failure: :retry, start: g, **params)
|
81
104
|
with_upsert_error_handling(on_failure, params) do
|
82
105
|
create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
|
83
|
-
|
106
|
+
start.upsertE(label, from, to, create_properties, update_properties).id.next
|
84
107
|
end
|
85
108
|
end
|
86
109
|
|
87
110
|
# edges:
|
88
111
|
# [["label", "from", "to", {create: :properties}, {update: properties}]]
|
89
112
|
# params can override Retryable config from UPSERT_RETRY_PARAMS
|
90
|
-
def upsert_edges(edges, batch_size: 100, on_failure: :retry, **params)
|
113
|
+
def upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)
|
91
114
|
edges.each_slice(batch_size) do |slice|
|
92
115
|
with_upsert_error_handling(on_failure, params) do
|
93
|
-
slice.reduce(
|
116
|
+
slice.reduce(start) do |t, (label, from, to, create_properties, update_properties)|
|
94
117
|
create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
|
95
118
|
|
96
119
|
t.upsertE(label, from, to, create_properties, update_properties)
|
@@ -18,11 +18,19 @@ module Grumlin
|
|
18
18
|
# {"detailedMessage":"",
|
19
19
|
# "requestId":"UUID",
|
20
20
|
# "code":"ConcurrentModificationException"}
|
21
|
-
#
|
21
|
+
# Currently we simply search for substrings to identify the exact error
|
22
22
|
# TODO: parse json and use `code` instead
|
23
|
+
|
23
24
|
VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
|
24
25
|
EDGE_ALREADY_EXISTS = "Edge with id already exists:"
|
26
|
+
|
25
27
|
CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
|
28
|
+
|
29
|
+
CONCURRENT_VERTEX_PROPERTY_INSERT_FAILED =
|
30
|
+
"Failed to complete Insert operation for a VertexProperty due to conflicting concurrent"
|
31
|
+
CONCURRENT_EDGE_PROPERTY_INSERT_FAILED =
|
32
|
+
"Failed to complete Insert operation for a EdgeProperty due to conflicting concurrent"
|
33
|
+
|
26
34
|
CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
|
27
35
|
CONCURRENCT_MODIFICATION_FAILED = "Failed to complete operation due to conflicting concurrent"
|
28
36
|
|
@@ -49,9 +57,15 @@ module Grumlin
|
|
49
57
|
return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
|
50
58
|
end
|
51
59
|
|
52
|
-
def concurrent_modification_error(status)
|
60
|
+
def concurrent_modification_error(status) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
53
61
|
return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
|
62
|
+
if status[:message]&.include?(CONCURRENT_VERTEX_PROPERTY_INSERT_FAILED)
|
63
|
+
return ConcurrentVertexPropertyInsertFailedError
|
64
|
+
end
|
54
65
|
return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
|
66
|
+
if status[:message]&.include?(CONCURRENT_EDGE_PROPERTY_INSERT_FAILED)
|
67
|
+
return ConcurrentEdgePropertyInsertFailedError
|
68
|
+
end
|
55
69
|
return ConcurrentModificationError if status[:message]&.include?(CONCURRENCT_MODIFICATION_FAILED)
|
56
70
|
end
|
57
71
|
end
|
data/lib/grumlin/steps.rb
CHANGED
@@ -32,7 +32,9 @@ module Grumlin
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def add(name, args: [], params: {})
|
35
|
-
|
35
|
+
if CONFIGURATION_STEPS.include?(name) || name.to_sym == :tx
|
36
|
+
return add_configuration_step(name, args: args, params: params)
|
37
|
+
end
|
36
38
|
|
37
39
|
StepData.new(name, args: cast_arguments(args), params: params).tap do |step|
|
38
40
|
@steps << step
|
@@ -10,17 +10,19 @@ module Grumlin
|
|
10
10
|
|
11
11
|
def serialize
|
12
12
|
steps = ShortcutsApplyer.call(@steps)
|
13
|
-
no_return = @params
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}.tap do |v|
|
18
|
-
v.merge!(source: steps.configuration_steps.map { |s| serialize_step(s) }) if steps.configuration_steps.any?
|
13
|
+
no_return = @params.fetch(:no_return, false)
|
14
|
+
{}.tap do |result|
|
15
|
+
result[:step] = serialize_steps(steps.steps + (no_return ? [NONE_STEP] : [])) if steps.steps.any?
|
16
|
+
result[:source] = serialize_steps(steps.configuration_steps) if steps.configuration_steps.any?
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
22
20
|
private
|
23
21
|
|
22
|
+
def serialize_steps(steps)
|
23
|
+
steps.map { |s| serialize_step(s) }
|
24
|
+
end
|
25
|
+
|
24
26
|
def serialize_step(step)
|
25
27
|
[step.name].tap do |result|
|
26
28
|
step.args.each do |arg|
|
data/lib/grumlin/transaction.rb
CHANGED
@@ -2,49 +2,37 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
class Transaction
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :session_id
|
6
6
|
|
7
7
|
include Console
|
8
8
|
|
9
|
+
COMMIT = Grumlin::Repository.new.g.step(:tx, :commit).bytecode
|
10
|
+
ROLLBACK = Grumlin::Repository.new.g.step(:tx, :rollback).bytecode
|
11
|
+
|
9
12
|
def initialize(traversal_start_class, pool: Grumlin.default_pool)
|
10
13
|
@traversal_start_class = traversal_start_class
|
11
14
|
@pool = pool
|
12
15
|
|
13
|
-
|
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?
|
16
|
+
@session_id = SecureRandom.uuid
|
25
17
|
end
|
26
18
|
|
27
19
|
def begin
|
28
|
-
@traversal_start_class.new(session_id: @
|
20
|
+
@traversal_start_class.new(session_id: @session_id)
|
29
21
|
end
|
30
22
|
|
31
23
|
def commit
|
32
|
-
|
33
|
-
|
34
|
-
finalize(:commit)
|
24
|
+
finalize(COMMIT)
|
35
25
|
end
|
36
26
|
|
37
27
|
def rollback
|
38
|
-
|
39
|
-
|
40
|
-
finalize(:rollback)
|
28
|
+
finalize(ROLLBACK)
|
41
29
|
end
|
42
30
|
|
43
31
|
private
|
44
32
|
|
45
33
|
def finalize(action)
|
46
34
|
@pool.acquire do |client|
|
47
|
-
client.
|
35
|
+
client.write(action, session_id: @session_id)
|
48
36
|
end
|
49
37
|
end
|
50
38
|
end
|
@@ -5,12 +5,24 @@ module Grumlin
|
|
5
5
|
include WithExtension
|
6
6
|
|
7
7
|
class TraversalError < Grumlin::Error; end
|
8
|
-
class
|
8
|
+
class AlreadyBoundToTransactionError < TraversalError; end
|
9
9
|
|
10
10
|
def tx
|
11
|
-
raise
|
11
|
+
raise AlreadyBoundToTransactionError if @session_id
|
12
12
|
|
13
|
-
|
13
|
+
transaction = tx_class.new(self.class, pool: @pool)
|
14
|
+
return transaction unless block_given?
|
15
|
+
|
16
|
+
begin
|
17
|
+
yield transaction.begin
|
18
|
+
rescue Grumlin::Rollback
|
19
|
+
transaction.rollback
|
20
|
+
rescue StandardError
|
21
|
+
transaction.rollback
|
22
|
+
raise
|
23
|
+
else
|
24
|
+
transaction.commit
|
25
|
+
end
|
14
26
|
end
|
15
27
|
|
16
28
|
def to_s(*)
|
@@ -20,5 +32,11 @@ module Grumlin
|
|
20
32
|
def inspect
|
21
33
|
self.class.inspect
|
22
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def tx_class
|
39
|
+
Grumlin.features.supports_transactions? ? Transaction : DummyTransaction
|
40
|
+
end
|
23
41
|
end
|
24
42
|
end
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
@@ -33,6 +33,9 @@ loader.do_not_eager_load(test_helpers)
|
|
33
33
|
module Grumlin
|
34
34
|
class Error < StandardError; end
|
35
35
|
|
36
|
+
class TransactionError < Error; end
|
37
|
+
class Rollback < TransactionError; end
|
38
|
+
|
36
39
|
class UnknownError < Error; end
|
37
40
|
|
38
41
|
class ConnectionError < Error; end
|
@@ -104,6 +107,9 @@ module Grumlin
|
|
104
107
|
class ConcurrentVertexInsertFailedError < ConcurrentInsertFailedError; end
|
105
108
|
class ConcurrentEdgeInsertFailedError < ConcurrentInsertFailedError; end
|
106
109
|
|
110
|
+
class ConcurrentVertexPropertyInsertFailedError < ConcurrentInsertFailedError; end
|
111
|
+
class ConcurrentEdgePropertyInsertFailedError < ConcurrentInsertFailedError; end
|
112
|
+
|
107
113
|
class ServerSerializationError < ServerSideError; end
|
108
114
|
|
109
115
|
class ServerTimeoutError < ServerSideError; end
|
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.
|
4
|
+
version: 0.22.2
|
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-08-
|
11
|
+
date: 2022-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- lib/grumlin/benchmark/repository.rb
|
118
118
|
- lib/grumlin/client.rb
|
119
119
|
- lib/grumlin/config.rb
|
120
|
+
- lib/grumlin/dummy_transaction.rb
|
120
121
|
- lib/grumlin/edge.rb
|
121
122
|
- lib/grumlin/expressions/cardinality.rb
|
122
123
|
- lib/grumlin/expressions/column.rb
|