restate-sdk 0.6.0 → 0.8.0
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/Cargo.lock +1 -1
- data/README.md +31 -14
- data/ext/restate_internal/Cargo.toml +1 -1
- data/lib/restate/client.rb +157 -0
- data/lib/restate/config.rb +35 -0
- data/lib/restate/context.rb +13 -128
- data/lib/restate/discovery.rb +6 -15
- data/lib/restate/durable_future.rb +15 -39
- data/lib/restate/endpoint.rb +5 -22
- data/lib/restate/errors.rb +2 -15
- data/lib/restate/handler.rb +21 -20
- data/lib/restate/serde.rb +14 -130
- data/lib/restate/server.rb +10 -26
- data/lib/restate/server_context.rb +50 -248
- data/lib/restate/service.rb +24 -3
- data/lib/restate/service_dsl.rb +70 -6
- data/lib/restate/service_proxy.rb +73 -0
- data/lib/restate/testing.rb +1 -1
- data/lib/restate/version.rb +2 -2
- data/lib/restate/virtual_object.rb +27 -4
- data/lib/restate/vm.rb +9 -74
- data/lib/restate/workflow.rb +27 -4
- data/lib/restate.rb +222 -62
- data/sig/restate.rbs +196 -0
- metadata +6 -18
- data/lib/tapioca/dsl/compilers/restate.rb +0 -116
- data/rbi/restate-sdk.rbi +0 -307
data/lib/restate/service.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: false
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Restate
|
|
@@ -11,7 +11,6 @@ module Restate
|
|
|
11
11
|
# end
|
|
12
12
|
# end
|
|
13
13
|
class Service
|
|
14
|
-
extend T::Sig
|
|
15
14
|
extend ServiceDSL
|
|
16
15
|
|
|
17
16
|
# Register a handler method on this service.
|
|
@@ -27,7 +26,29 @@ module Restate
|
|
|
27
26
|
end
|
|
28
27
|
return method_name unless method_name.is_a?(Symbol)
|
|
29
28
|
|
|
30
|
-
_register_handler(method_name,
|
|
29
|
+
_register_handler(method_name, kind: nil, **opts)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns a call proxy for fluent durable calls to this service.
|
|
33
|
+
#
|
|
34
|
+
# @example
|
|
35
|
+
# Greeter.call.greet("World").await
|
|
36
|
+
#
|
|
37
|
+
# @return [ServiceCallProxy]
|
|
38
|
+
def self.call
|
|
39
|
+
ServiceCallProxy.new(self, call_method: :service_call)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns a send proxy for fluent fire-and-forget sends to this service.
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# Greeter.send!.greet("World")
|
|
46
|
+
# Greeter.send!(delay: 60).greet("World")
|
|
47
|
+
#
|
|
48
|
+
# @param delay [Numeric, nil] optional delay in seconds
|
|
49
|
+
# @return [ServiceSendProxy]
|
|
50
|
+
def self.send!(delay: nil)
|
|
51
|
+
ServiceSendProxy.new(self, send_method: :service_send, delay: delay)
|
|
31
52
|
end
|
|
32
53
|
|
|
33
54
|
def self._service_kind
|
data/lib/restate/service_dsl.rb
CHANGED
|
@@ -31,6 +31,70 @@ module Restate
|
|
|
31
31
|
subclass.instance_variable_set(:@_idempotency_retention, nil)
|
|
32
32
|
subclass.instance_variable_set(:@_ingress_private, nil)
|
|
33
33
|
subclass.instance_variable_set(:@_invocation_retry_policy, nil)
|
|
34
|
+
subclass.instance_variable_set(:@_state_declarations, {})
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Declare a durable state entry with auto-generated getter, setter, and clear methods.
|
|
38
|
+
# Only available on VirtualObject and Workflow.
|
|
39
|
+
#
|
|
40
|
+
# The generated methods delegate to the current Restate context via +Thread.current+
|
|
41
|
+
# (fiber-scoped in Ruby 3.0+), so they work correctly across concurrent invocations.
|
|
42
|
+
#
|
|
43
|
+
# @param name [Symbol] state key name
|
|
44
|
+
# @param default [Object, nil] default value returned when state is not set
|
|
45
|
+
# @param serde [Object] serializer/deserializer (defaults to JsonSerde)
|
|
46
|
+
#
|
|
47
|
+
# @example
|
|
48
|
+
# class Counter < Restate::VirtualObject
|
|
49
|
+
# state :count, default: 0
|
|
50
|
+
#
|
|
51
|
+
# handler def add(ctx, addend)
|
|
52
|
+
# self.count += addend # reads then writes via ctx.get/ctx.set
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# shared def get(ctx)
|
|
56
|
+
# count # reads via ctx.get, returns 0 if unset
|
|
57
|
+
# end
|
|
58
|
+
# end
|
|
59
|
+
def state(name, default: nil, serde: nil) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
60
|
+
unless respond_to?(:_service_kind) && %w[object workflow].include?(_service_kind)
|
|
61
|
+
Kernel.raise ArgumentError, 'state declarations are only available on VirtualObject and Workflow'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
name = name.to_sym
|
|
65
|
+
@_state_declarations[name] = { default: default, serde: serde }
|
|
66
|
+
state_key = name.to_s
|
|
67
|
+
state_serde = serde
|
|
68
|
+
state_default = default
|
|
69
|
+
|
|
70
|
+
# Getter: reads from durable state, returns default if unset
|
|
71
|
+
define_method(name) do
|
|
72
|
+
ctx = Thread.current[:restate_context]
|
|
73
|
+
Kernel.raise 'Not inside a Restate handler' unless ctx
|
|
74
|
+
|
|
75
|
+
val = state_serde ? ctx.get(state_key, serde: state_serde) : ctx.get(state_key)
|
|
76
|
+
val.nil? ? state_default : val
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Setter: writes to durable state
|
|
80
|
+
define_method(:"#{name}=") do |value|
|
|
81
|
+
ctx = Thread.current[:restate_context]
|
|
82
|
+
Kernel.raise 'Not inside a Restate handler' unless ctx
|
|
83
|
+
|
|
84
|
+
if state_serde
|
|
85
|
+
ctx.set(state_key, value, serde: state_serde)
|
|
86
|
+
else
|
|
87
|
+
ctx.set(state_key, value)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Clear: removes the state entry
|
|
92
|
+
define_method(:"clear_#{name}") do
|
|
93
|
+
ctx = Thread.current[:restate_context]
|
|
94
|
+
Kernel.raise 'Not inside a Restate handler' unless ctx
|
|
95
|
+
|
|
96
|
+
ctx.clear(state_key)
|
|
97
|
+
end
|
|
34
98
|
end
|
|
35
99
|
|
|
36
100
|
# Get or set the service name. Defaults to the unqualified class name.
|
|
@@ -41,7 +105,7 @@ module Restate
|
|
|
41
105
|
if name
|
|
42
106
|
@_service_name = name
|
|
43
107
|
else
|
|
44
|
-
@_service_name
|
|
108
|
+
@_service_name ||= self.name&.split('::')&.last # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
45
109
|
end
|
|
46
110
|
end
|
|
47
111
|
|
|
@@ -135,7 +199,7 @@ module Restate
|
|
|
135
199
|
#
|
|
136
200
|
# @return [ServiceTag]
|
|
137
201
|
def service_tag
|
|
138
|
-
ServiceTag.new(kind:
|
|
202
|
+
ServiceTag.new(kind: _service_kind, name: service_name,
|
|
139
203
|
description: @_description, metadata: @_metadata)
|
|
140
204
|
end
|
|
141
205
|
|
|
@@ -195,7 +259,7 @@ module Restate
|
|
|
195
259
|
def _build_handlers # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
196
260
|
tag = service_tag
|
|
197
261
|
result = {}
|
|
198
|
-
instance =
|
|
262
|
+
instance = allocate
|
|
199
263
|
|
|
200
264
|
@_handler_registry.each do |name, meta| # rubocop:disable Metrics/BlockLength
|
|
201
265
|
input_serde = Serde.resolve(meta[:input])
|
|
@@ -208,10 +272,10 @@ module Restate
|
|
|
208
272
|
output_serde: output_serde
|
|
209
273
|
)
|
|
210
274
|
|
|
211
|
-
um =
|
|
275
|
+
um = instance_method(name)
|
|
212
276
|
arity = um.arity.abs
|
|
213
|
-
unless [
|
|
214
|
-
Kernel.raise ArgumentError, "handler '#{name}' must accept
|
|
277
|
+
unless [0, 1].include?(arity)
|
|
278
|
+
Kernel.raise ArgumentError, "handler '#{name}' must accept 0 or 1 parameters ([input]), got #{arity}"
|
|
215
279
|
end
|
|
216
280
|
|
|
217
281
|
bound = um.bind(instance)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Restate
|
|
5
|
+
# Proxy for fluent durable calls: +Service.call.handler(arg)+
|
|
6
|
+
#
|
|
7
|
+
# Returned by the +.call+ class method on service classes. Uses +method_missing+
|
|
8
|
+
# to forward handler invocations to the current Restate context.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # Instead of: ctx.service_call(Greeter, :greet, "World")
|
|
12
|
+
# Greeter.call.greet("World")
|
|
13
|
+
#
|
|
14
|
+
# # Instead of: ctx.object_call(Counter, :add, "key", 5)
|
|
15
|
+
# Counter.call("key").add(5)
|
|
16
|
+
#
|
|
17
|
+
# @!visibility private
|
|
18
|
+
class ServiceCallProxy
|
|
19
|
+
def initialize(service_class, key: nil, call_method: :service_call)
|
|
20
|
+
@service_class = service_class
|
|
21
|
+
@key = key
|
|
22
|
+
@call_method = call_method
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def method_missing(handler_name, arg = nil, **opts)
|
|
26
|
+
ctx = Restate.fetch_context!
|
|
27
|
+
if @key
|
|
28
|
+
ctx.public_send(@call_method, @service_class, handler_name, @key, arg, **opts)
|
|
29
|
+
else
|
|
30
|
+
ctx.public_send(@call_method, @service_class, handler_name, arg, **opts)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
35
|
+
(@service_class.respond_to?(:handlers) && @service_class.handlers.key?(method_name.to_s)) || super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Proxy for fluent fire-and-forget sends: +Service.send!.handler(arg)+
|
|
40
|
+
#
|
|
41
|
+
# Returned by the +.send!+ class method on service classes.
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# # Instead of: ctx.service_send(Greeter, :greet, "World")
|
|
45
|
+
# Greeter.send!.greet("World")
|
|
46
|
+
#
|
|
47
|
+
# # Instead of: ctx.object_send(Counter, :add, "key", 5, delay: 60)
|
|
48
|
+
# Counter.send!("key", delay: 60).add(5)
|
|
49
|
+
#
|
|
50
|
+
# @!visibility private
|
|
51
|
+
class ServiceSendProxy
|
|
52
|
+
def initialize(service_class, key: nil, send_method: :service_send, delay: nil)
|
|
53
|
+
@service_class = service_class
|
|
54
|
+
@key = key
|
|
55
|
+
@send_method = send_method
|
|
56
|
+
@delay = delay
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def method_missing(handler_name, arg = nil, **opts)
|
|
60
|
+
ctx = Restate.fetch_context!
|
|
61
|
+
opts[:delay] = @delay if @delay
|
|
62
|
+
if @key
|
|
63
|
+
ctx.public_send(@send_method, @service_class, handler_name, @key, arg, **opts)
|
|
64
|
+
else
|
|
65
|
+
ctx.public_send(@send_method, @service_class, handler_name, arg, **opts)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
70
|
+
(@service_class.respond_to?(:handlers) && @service_class.handlers.key?(method_name.to_s)) || super
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/restate/testing.rb
CHANGED
data/lib/restate/version.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: false
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Restate
|
|
@@ -17,7 +17,6 @@ module Restate
|
|
|
17
17
|
# end
|
|
18
18
|
# end
|
|
19
19
|
class VirtualObject
|
|
20
|
-
extend T::Sig
|
|
21
20
|
extend ServiceDSL
|
|
22
21
|
|
|
23
22
|
# Register an exclusive handler. Use as: +handler def my_method(ctx, arg)+
|
|
@@ -33,7 +32,7 @@ module Restate
|
|
|
33
32
|
end
|
|
34
33
|
return method_name unless method_name.is_a?(Symbol)
|
|
35
34
|
|
|
36
|
-
_register_handler(method_name,
|
|
35
|
+
_register_handler(method_name, kind: kind.to_s, **opts)
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
# Register a shared (concurrent-access) handler.
|
|
@@ -46,7 +45,31 @@ module Restate
|
|
|
46
45
|
"handler expects a Symbol (use `shared def #{method_name}(...)` or `shared :#{method_name}`)"
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
_register_handler(method_name,
|
|
48
|
+
_register_handler(method_name, kind: 'shared', **opts)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns a call proxy for fluent durable calls to this virtual object.
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# Counter.call("my-key").add(5).await
|
|
55
|
+
#
|
|
56
|
+
# @param key [String] the object key
|
|
57
|
+
# @return [ServiceCallProxy]
|
|
58
|
+
def self.call(key)
|
|
59
|
+
ServiceCallProxy.new(self, key: key, call_method: :object_call)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns a send proxy for fluent fire-and-forget sends to this virtual object.
|
|
63
|
+
#
|
|
64
|
+
# @example
|
|
65
|
+
# Counter.send!("my-key").add(5)
|
|
66
|
+
# Counter.send!("my-key", delay: 60).add(5)
|
|
67
|
+
#
|
|
68
|
+
# @param key [String] the object key
|
|
69
|
+
# @param delay [Numeric, nil] optional delay in seconds
|
|
70
|
+
# @return [ServiceSendProxy]
|
|
71
|
+
def self.send!(key, delay: nil)
|
|
72
|
+
ServiceSendProxy.new(self, key: key, send_method: :object_send, delay: delay)
|
|
50
73
|
end
|
|
51
74
|
|
|
52
75
|
def self._service_kind
|
data/lib/restate/vm.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: ignore
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
begin
|
|
@@ -19,9 +19,9 @@ module Restate
|
|
|
19
19
|
class NotReady; end
|
|
20
20
|
class Suspended; end
|
|
21
21
|
|
|
22
|
-
NOT_READY =
|
|
23
|
-
SUSPENDED =
|
|
24
|
-
CANCEL_HANDLE =
|
|
22
|
+
NOT_READY = NotReady.new.freeze
|
|
23
|
+
SUSPENDED = Suspended.new.freeze
|
|
24
|
+
CANCEL_HANDLE = Internal::CANCEL_NOTIFICATION_HANDLE
|
|
25
25
|
|
|
26
26
|
# Progress loop result types
|
|
27
27
|
class DoProgressAnyCompleted; end
|
|
@@ -29,10 +29,10 @@ module Restate
|
|
|
29
29
|
class DoProgressCancelSignalReceived; end
|
|
30
30
|
class DoWaitPendingRun; end
|
|
31
31
|
|
|
32
|
-
DO_PROGRESS_ANY_COMPLETED =
|
|
33
|
-
DO_PROGRESS_READ_FROM_INPUT =
|
|
34
|
-
DO_PROGRESS_CANCEL_SIGNAL_RECEIVED =
|
|
35
|
-
DO_WAIT_PENDING_RUN =
|
|
32
|
+
DO_PROGRESS_ANY_COMPLETED = DoProgressAnyCompleted.new.freeze
|
|
33
|
+
DO_PROGRESS_READ_FROM_INPUT = DoProgressReadFromInput.new.freeze
|
|
34
|
+
DO_PROGRESS_CANCEL_SIGNAL_RECEIVED = DoProgressCancelSignalReceived.new.freeze
|
|
35
|
+
DO_WAIT_PENDING_RUN = DoWaitPendingRun.new.freeze
|
|
36
36
|
|
|
37
37
|
DoProgressExecuteRun = Struct.new(:handle, keyword_init: true)
|
|
38
38
|
|
|
@@ -52,50 +52,39 @@ module Restate
|
|
|
52
52
|
|
|
53
53
|
# Wraps the native Restate::Internal::VM, mapping native types to Ruby types.
|
|
54
54
|
class VMWrapper
|
|
55
|
-
extend T::Sig
|
|
56
|
-
|
|
57
|
-
sig { params(headers: T.untyped).void }
|
|
58
55
|
def initialize(headers)
|
|
59
|
-
@vm =
|
|
56
|
+
@vm = Internal::VM.new(headers)
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
sig { returns([Integer, T.untyped]) }
|
|
63
59
|
def get_response_head
|
|
64
60
|
result = @vm.get_response_head
|
|
65
61
|
[result.status_code, result.headers]
|
|
66
62
|
end
|
|
67
63
|
|
|
68
|
-
sig { params(buf: String).void }
|
|
69
64
|
def notify_input(buf)
|
|
70
65
|
@vm.notify_input(buf)
|
|
71
66
|
end
|
|
72
67
|
|
|
73
|
-
sig { void }
|
|
74
68
|
def notify_input_closed
|
|
75
69
|
@vm.notify_input_closed
|
|
76
70
|
end
|
|
77
71
|
|
|
78
|
-
sig { params(error: String, stacktrace: T.nilable(String)).void }
|
|
79
72
|
def notify_error(error, stacktrace = nil)
|
|
80
73
|
@vm.notify_error(error, stacktrace)
|
|
81
74
|
end
|
|
82
75
|
|
|
83
|
-
sig { returns(T.nilable(String)) }
|
|
84
76
|
def take_output
|
|
85
77
|
@vm.take_output
|
|
86
78
|
end
|
|
87
79
|
|
|
88
|
-
sig { returns(T::Boolean) }
|
|
89
80
|
def is_ready_to_execute
|
|
90
81
|
@vm.is_ready_to_execute
|
|
91
82
|
end
|
|
92
83
|
|
|
93
|
-
sig { params(handle: Integer).returns(T::Boolean) }
|
|
94
84
|
def is_completed(handle)
|
|
95
85
|
@vm.is_completed(handle)
|
|
96
86
|
end
|
|
97
87
|
|
|
98
|
-
sig { params(handles: T::Array[Integer]).returns(T.untyped) }
|
|
99
88
|
def do_progress(handles)
|
|
100
89
|
result = @vm.do_progress(handles)
|
|
101
90
|
map_do_progress(result)
|
|
@@ -103,7 +92,6 @@ module Restate
|
|
|
103
92
|
e
|
|
104
93
|
end
|
|
105
94
|
|
|
106
|
-
sig { params(handle: Integer).returns(T.untyped) }
|
|
107
95
|
def take_notification(handle)
|
|
108
96
|
result = @vm.take_notification(handle)
|
|
109
97
|
map_notification(result)
|
|
@@ -111,7 +99,6 @@ module Restate
|
|
|
111
99
|
e
|
|
112
100
|
end
|
|
113
101
|
|
|
114
|
-
sig { returns(T.untyped) }
|
|
115
102
|
def sys_input
|
|
116
103
|
inp = @vm.sys_input
|
|
117
104
|
headers = inp.headers.map { |h| [h.key, h.value] }
|
|
@@ -124,94 +111,56 @@ module Restate
|
|
|
124
111
|
)
|
|
125
112
|
end
|
|
126
113
|
|
|
127
|
-
sig { params(name: String).returns(Integer) }
|
|
128
114
|
def sys_get_state(name)
|
|
129
115
|
@vm.sys_get_state(name)
|
|
130
116
|
end
|
|
131
117
|
|
|
132
|
-
sig { returns(Integer) }
|
|
133
118
|
def sys_get_state_keys
|
|
134
119
|
@vm.sys_get_state_keys
|
|
135
120
|
end
|
|
136
121
|
|
|
137
|
-
sig { params(name: String, value: String).void }
|
|
138
122
|
def sys_set_state(name, value)
|
|
139
123
|
@vm.sys_set_state(name, value)
|
|
140
124
|
end
|
|
141
125
|
|
|
142
|
-
sig { params(name: String).void }
|
|
143
126
|
def sys_clear_state(name)
|
|
144
127
|
@vm.sys_clear_state(name)
|
|
145
128
|
end
|
|
146
129
|
|
|
147
|
-
sig { void }
|
|
148
130
|
def sys_clear_all_state
|
|
149
131
|
@vm.sys_clear_all_state
|
|
150
132
|
end
|
|
151
133
|
|
|
152
|
-
sig { params(millis: Integer, name: T.nilable(String)).returns(Integer) }
|
|
153
134
|
def sys_sleep(millis, name = nil)
|
|
154
135
|
# Rust side always expects 2 args: (millis, name_or_nil)
|
|
155
136
|
@vm.sys_sleep(millis, name)
|
|
156
137
|
end
|
|
157
138
|
|
|
158
|
-
sig do
|
|
159
|
-
params(
|
|
160
|
-
service: String,
|
|
161
|
-
handler: String,
|
|
162
|
-
parameter: String,
|
|
163
|
-
key: T.nilable(String),
|
|
164
|
-
idempotency_key: T.nilable(String),
|
|
165
|
-
headers: T.nilable(T::Hash[String, String])
|
|
166
|
-
).returns(Internal::CallHandle)
|
|
167
|
-
end
|
|
168
139
|
def sys_call(service:, handler:, parameter:, key: nil, idempotency_key: nil, headers: nil)
|
|
169
140
|
# Rust side expects 6 args: (service, handler, buffer, key_or_nil, idem_key_or_nil, headers_or_nil)
|
|
170
141
|
hdr_array = headers&.map { |k, v| [k, v] }
|
|
171
142
|
@vm.sys_call(service, handler, parameter, key, idempotency_key, hdr_array)
|
|
172
143
|
end
|
|
173
144
|
|
|
174
|
-
sig do
|
|
175
|
-
params(
|
|
176
|
-
service: String,
|
|
177
|
-
handler: String,
|
|
178
|
-
parameter: String,
|
|
179
|
-
key: T.nilable(String),
|
|
180
|
-
delay: T.nilable(Integer),
|
|
181
|
-
idempotency_key: T.nilable(String),
|
|
182
|
-
headers: T.nilable(T::Hash[String, String])
|
|
183
|
-
).returns(Integer)
|
|
184
|
-
end
|
|
185
145
|
def sys_send(service:, handler:, parameter:, key: nil, delay: nil, idempotency_key: nil, headers: nil)
|
|
186
146
|
# Rust side expects 7 args
|
|
187
147
|
hdr_array = headers&.map { |k, v| [k, v] }
|
|
188
148
|
@vm.sys_send(service, handler, parameter, key, delay, idempotency_key, hdr_array)
|
|
189
149
|
end
|
|
190
150
|
|
|
191
|
-
sig { params(name: String).returns(Integer) }
|
|
192
151
|
def sys_run(name)
|
|
193
152
|
@vm.sys_run(name)
|
|
194
153
|
end
|
|
195
154
|
|
|
196
|
-
sig { params(handle: Integer, output: String).void }
|
|
197
155
|
def propose_run_completion_success(handle, output)
|
|
198
156
|
@vm.propose_run_completion_success(handle, output)
|
|
199
157
|
end
|
|
200
158
|
|
|
201
|
-
sig { params(handle: Integer, failure: T.untyped).void }
|
|
202
159
|
def propose_run_completion_failure(handle, failure)
|
|
203
160
|
native_failure = Internal::Failure.new(failure.code, failure.message, nil)
|
|
204
161
|
@vm.propose_run_completion_failure(handle, native_failure)
|
|
205
162
|
end
|
|
206
163
|
|
|
207
|
-
sig do
|
|
208
|
-
params(
|
|
209
|
-
handle: Integer,
|
|
210
|
-
failure: T.untyped,
|
|
211
|
-
attempt_duration_ms: Integer,
|
|
212
|
-
config: T.untyped
|
|
213
|
-
).void
|
|
214
|
-
end
|
|
215
164
|
def propose_run_completion_transient(handle, failure:, attempt_duration_ms:, config:)
|
|
216
165
|
native_failure = Internal::Failure.new(failure.code, failure.message, failure.stacktrace)
|
|
217
166
|
native_config = Internal::ExponentialRetryConfig.new(
|
|
@@ -222,73 +171,60 @@ module Restate
|
|
|
222
171
|
@vm.propose_run_completion_failure_transient(handle, native_failure, attempt_duration_ms, native_config)
|
|
223
172
|
end
|
|
224
173
|
|
|
225
|
-
sig { params(output: String).void }
|
|
226
174
|
def sys_write_output_success(output)
|
|
227
175
|
@vm.sys_write_output_success(output)
|
|
228
176
|
end
|
|
229
177
|
|
|
230
|
-
sig { params(failure: T.untyped).void }
|
|
231
178
|
def sys_write_output_failure(failure)
|
|
232
179
|
native_failure = Internal::Failure.new(failure.code, failure.message, nil)
|
|
233
180
|
@vm.sys_write_output_failure(native_failure)
|
|
234
181
|
end
|
|
235
182
|
|
|
236
|
-
sig { void }
|
|
237
183
|
def sys_end
|
|
238
184
|
@vm.sys_end
|
|
239
185
|
end
|
|
240
186
|
|
|
241
|
-
sig { returns(T::Boolean) }
|
|
242
187
|
def is_replaying
|
|
243
188
|
@vm.is_replaying
|
|
244
189
|
end
|
|
245
190
|
|
|
246
191
|
# Returns [awakeable_id (String), notification_handle (Integer)]
|
|
247
|
-
sig { returns([String, Integer]) }
|
|
248
192
|
def sys_awakeable
|
|
249
193
|
@vm.sys_awakeable
|
|
250
194
|
end
|
|
251
195
|
|
|
252
|
-
sig { params(awakeable_id: String, value: String).void }
|
|
253
196
|
def sys_complete_awakeable_success(awakeable_id, value)
|
|
254
197
|
@vm.sys_complete_awakeable_success(awakeable_id, value)
|
|
255
198
|
end
|
|
256
199
|
|
|
257
|
-
sig { params(awakeable_id: String, failure: T.untyped).void }
|
|
258
200
|
def sys_complete_awakeable_failure(awakeable_id, failure)
|
|
259
201
|
native_failure = Internal::Failure.new(failure.code, failure.message, nil)
|
|
260
202
|
@vm.sys_complete_awakeable_failure(awakeable_id, native_failure)
|
|
261
203
|
end
|
|
262
204
|
|
|
263
|
-
sig { params(key: String).returns(Integer) }
|
|
264
205
|
def sys_get_promise(key)
|
|
265
206
|
@vm.sys_get_promise(key)
|
|
266
207
|
end
|
|
267
208
|
|
|
268
|
-
sig { params(key: String).returns(Integer) }
|
|
269
209
|
def sys_peek_promise(key)
|
|
270
210
|
@vm.sys_peek_promise(key)
|
|
271
211
|
end
|
|
272
212
|
|
|
273
|
-
sig { params(key: String, value: String).returns(Integer) }
|
|
274
213
|
def sys_complete_promise_success(key, value)
|
|
275
214
|
@vm.sys_complete_promise_success(key, value)
|
|
276
215
|
end
|
|
277
216
|
|
|
278
|
-
sig { params(key: String, failure: T.untyped).returns(Integer) }
|
|
279
217
|
def sys_complete_promise_failure(key, failure)
|
|
280
218
|
native_failure = Internal::Failure.new(failure.code, failure.message, nil)
|
|
281
219
|
@vm.sys_complete_promise_failure(key, native_failure)
|
|
282
220
|
end
|
|
283
221
|
|
|
284
|
-
sig { params(invocation_id: String).void }
|
|
285
222
|
def sys_cancel_invocation(invocation_id)
|
|
286
223
|
@vm.sys_cancel_invocation(invocation_id)
|
|
287
224
|
end
|
|
288
225
|
|
|
289
226
|
private
|
|
290
227
|
|
|
291
|
-
sig { params(result: T.untyped).returns(T.untyped) }
|
|
292
228
|
def map_do_progress(result)
|
|
293
229
|
case result
|
|
294
230
|
when Internal::Suspended
|
|
@@ -308,7 +244,6 @@ module Restate
|
|
|
308
244
|
end
|
|
309
245
|
end
|
|
310
246
|
|
|
311
|
-
sig { params(result: T.untyped).returns(T.untyped) }
|
|
312
247
|
def map_notification(result)
|
|
313
248
|
case result
|
|
314
249
|
when Internal::Suspended
|
data/lib/restate/workflow.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: false
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Restate
|
|
@@ -15,7 +15,6 @@ module Restate
|
|
|
15
15
|
# end
|
|
16
16
|
# end
|
|
17
17
|
class Workflow
|
|
18
|
-
extend T::Sig
|
|
19
18
|
extend ServiceDSL
|
|
20
19
|
|
|
21
20
|
# Register the main workflow entry point.
|
|
@@ -31,7 +30,7 @@ module Restate
|
|
|
31
30
|
end
|
|
32
31
|
return method_name unless method_name.is_a?(Symbol)
|
|
33
32
|
|
|
34
|
-
_register_handler(method_name,
|
|
33
|
+
_register_handler(method_name, kind: 'workflow', **opts)
|
|
35
34
|
end
|
|
36
35
|
|
|
37
36
|
# Register a shared handler on this workflow.
|
|
@@ -46,7 +45,31 @@ module Restate
|
|
|
46
45
|
end
|
|
47
46
|
return method_name unless method_name.is_a?(Symbol)
|
|
48
47
|
|
|
49
|
-
_register_handler(method_name,
|
|
48
|
+
_register_handler(method_name, kind: 'shared', **opts)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns a call proxy for fluent durable calls to this workflow.
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# UserSignup.call("user42").run("user@example.com").await
|
|
55
|
+
#
|
|
56
|
+
# @param key [String] the workflow key
|
|
57
|
+
# @return [ServiceCallProxy]
|
|
58
|
+
def self.call(key)
|
|
59
|
+
ServiceCallProxy.new(self, key: key, call_method: :workflow_call)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns a send proxy for fluent fire-and-forget sends to this workflow.
|
|
63
|
+
#
|
|
64
|
+
# @example
|
|
65
|
+
# UserSignup.send!("user42").run("user@example.com")
|
|
66
|
+
# UserSignup.send!("user42", delay: 60).run("user@example.com")
|
|
67
|
+
#
|
|
68
|
+
# @param key [String] the workflow key
|
|
69
|
+
# @param delay [Numeric, nil] optional delay in seconds
|
|
70
|
+
# @return [ServiceSendProxy]
|
|
71
|
+
def self.send!(key, delay: nil)
|
|
72
|
+
ServiceSendProxy.new(self, key: key, send_method: :workflow_send, delay: delay)
|
|
50
73
|
end
|
|
51
74
|
|
|
52
75
|
def self._service_kind
|