restate-sdk 0.8.0 → 0.9.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/ext/restate_internal/Cargo.toml +1 -1
- data/lib/restate/endpoint.rb +57 -27
- data/lib/restate/server.rb +2 -1
- data/lib/restate/server_context.rb +67 -34
- data/lib/restate/version.rb +1 -1
- data/sig/restate.rbs +6 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fdc1d0db30055117693522a2f51fc194ed403a53b6069b2a69181c859b0e9f9
|
|
4
|
+
data.tar.gz: c39fde762f884f79335330428ba8e9d2bc07b86ba77b12d268db1cd727660cab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f460e546e7fb00ca7b6319e55374a52a944c58fcd3b0f0f399a62342e12d420280934473d0360d73d26be7dfd525cec7c4bfa831e198d90cb088ff6855351a1
|
|
7
|
+
data.tar.gz: 14361197f0c9bf7b798637d4a4e617d8438fdc46c433670eca069de100772e2126f8a0a41b27f0a7a283227f452c056aef8fce6efdaf0d60e5b86ef88a4fd1eb
|
data/Cargo.lock
CHANGED
data/lib/restate/endpoint.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
module Restate
|
|
5
5
|
# Container for registered services. Bind services here, then create the Rack app.
|
|
6
6
|
class Endpoint
|
|
7
|
-
attr_reader :services, :identity_keys, :middleware
|
|
7
|
+
attr_reader :services, :identity_keys, :middleware, :outbound_middleware
|
|
8
8
|
|
|
9
9
|
attr_accessor :protocol
|
|
10
10
|
|
|
@@ -13,6 +13,7 @@ module Restate
|
|
|
13
13
|
@protocol = nil
|
|
14
14
|
@identity_keys = []
|
|
15
15
|
@middleware = []
|
|
16
|
+
@outbound_middleware = []
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
# Bind one or more services to this endpoint.
|
|
@@ -48,29 +49,25 @@ module Restate
|
|
|
48
49
|
self
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
# Add
|
|
52
|
+
# Add inbound (server) middleware.
|
|
52
53
|
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
54
|
+
# Inbound middleware wraps every handler invocation, like
|
|
55
|
+
# {https://github.com/sidekiq/sidekiq/wiki/Middleware Sidekiq server middleware}.
|
|
55
56
|
#
|
|
56
57
|
# A middleware is a class whose instances respond to +call(handler, ctx)+.
|
|
57
58
|
# Use +yield+ inside +call+ to invoke the next middleware or the handler.
|
|
58
59
|
# The return value of +yield+ is the handler's return value.
|
|
59
60
|
#
|
|
60
|
-
# This follows the same pattern as {https://github.com/sidekiq/sidekiq/wiki/Middleware Sidekiq middleware}.
|
|
61
|
-
#
|
|
62
61
|
# @example OpenTelemetry tracing
|
|
63
|
-
# class
|
|
62
|
+
# class TracingMiddleware
|
|
64
63
|
# def call(handler, ctx)
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# }) do
|
|
69
|
-
# yield
|
|
64
|
+
# extracted = OpenTelemetry.propagation.extract(ctx.request.headers)
|
|
65
|
+
# OpenTelemetry::Context.with_current(extracted) do
|
|
66
|
+
# tracer.in_span(handler.name) { yield }
|
|
70
67
|
# end
|
|
71
68
|
# end
|
|
72
69
|
# end
|
|
73
|
-
# endpoint.use(
|
|
70
|
+
# endpoint.use(TracingMiddleware)
|
|
74
71
|
#
|
|
75
72
|
# @example Metrics
|
|
76
73
|
# class MetricsMiddleware
|
|
@@ -84,30 +81,53 @@ module Restate
|
|
|
84
81
|
# end
|
|
85
82
|
# endpoint.use(MetricsMiddleware)
|
|
86
83
|
#
|
|
87
|
-
# @
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
84
|
+
# @param klass [Class] middleware class (will be instantiated by the SDK)
|
|
85
|
+
# @param args [Array] positional arguments for the middleware constructor
|
|
86
|
+
# @param kwargs [Hash] keyword arguments for the middleware constructor
|
|
87
|
+
# @return [self]
|
|
88
|
+
def use(klass, *args, **kwargs)
|
|
89
|
+
@middleware << instantiate_middleware(klass, args, kwargs)
|
|
90
|
+
self
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Add outbound (client) middleware.
|
|
94
|
+
#
|
|
95
|
+
# Outbound middleware wraps every outgoing service call and send, like
|
|
96
|
+
# {https://github.com/sidekiq/sidekiq/wiki/Middleware Sidekiq client middleware}.
|
|
97
|
+
#
|
|
98
|
+
# A middleware is a class whose instances respond to +call(service, handler, headers)+.
|
|
99
|
+
# The +headers+ hash is mutable — modify it to attach headers to the outgoing
|
|
100
|
+
# request. Use +yield+ to continue the chain.
|
|
101
|
+
#
|
|
102
|
+
# Note: Restate automatically propagates inbound headers to outbound calls.
|
|
103
|
+
# Outbound middleware is for injecting *new* headers that aren't on the
|
|
104
|
+
# original request (e.g., tenant IDs from fiber-local storage, authorization
|
|
105
|
+
# tokens for specific target services).
|
|
106
|
+
#
|
|
107
|
+
# @example Propagate tenant ID to all outgoing calls
|
|
108
|
+
# class TenantOutboundMiddleware
|
|
109
|
+
# def call(_service, _handler, headers)
|
|
110
|
+
# headers['x-tenant-id'] = Thread.current[:tenant_id]
|
|
111
|
+
# yield
|
|
91
112
|
# end
|
|
113
|
+
# end
|
|
114
|
+
# endpoint.use_outbound(TenantOutboundMiddleware)
|
|
92
115
|
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
116
|
+
# @example Log all outgoing calls
|
|
117
|
+
# class OutboundLogger
|
|
118
|
+
# def call(service, handler, headers)
|
|
119
|
+
# logger.info("Calling #{service}/#{handler}")
|
|
95
120
|
# yield
|
|
96
121
|
# end
|
|
97
122
|
# end
|
|
98
|
-
# endpoint.
|
|
123
|
+
# endpoint.use_outbound(OutboundLogger)
|
|
99
124
|
#
|
|
100
125
|
# @param klass [Class] middleware class (will be instantiated by the SDK)
|
|
101
126
|
# @param args [Array] positional arguments for the middleware constructor
|
|
102
127
|
# @param kwargs [Hash] keyword arguments for the middleware constructor
|
|
103
128
|
# @return [self]
|
|
104
|
-
def
|
|
105
|
-
|
|
106
|
-
klass.new(*args)
|
|
107
|
-
else
|
|
108
|
-
klass.new(*args, **kwargs)
|
|
109
|
-
end
|
|
110
|
-
@middleware << instance
|
|
129
|
+
def use_outbound(klass, *args, **kwargs)
|
|
130
|
+
@outbound_middleware << instantiate_middleware(klass, args, kwargs)
|
|
111
131
|
self
|
|
112
132
|
end
|
|
113
133
|
|
|
@@ -116,5 +136,15 @@ module Restate
|
|
|
116
136
|
require_relative 'server'
|
|
117
137
|
Server.new(self)
|
|
118
138
|
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def instantiate_middleware(klass, args, kwargs)
|
|
143
|
+
if kwargs.empty?
|
|
144
|
+
klass.new(*args)
|
|
145
|
+
else
|
|
146
|
+
klass.new(*args, **kwargs)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
119
149
|
end
|
|
120
150
|
end
|
data/lib/restate/server.rb
CHANGED
|
@@ -193,7 +193,8 @@ module Restate
|
|
|
193
193
|
invocation: invocation,
|
|
194
194
|
send_output: send_output,
|
|
195
195
|
input_queue: input_queue,
|
|
196
|
-
middleware: @endpoint.middleware
|
|
196
|
+
middleware: @endpoint.middleware,
|
|
197
|
+
outbound_middleware: @endpoint.outbound_middleware
|
|
197
198
|
)
|
|
198
199
|
|
|
199
200
|
# Spawn the handler as an async task so the response body can stream
|
|
@@ -23,7 +23,8 @@ module Restate
|
|
|
23
23
|
|
|
24
24
|
attr_reader :vm, :invocation
|
|
25
25
|
|
|
26
|
-
def initialize(vm:, handler:, invocation:, send_output:, input_queue:, middleware: []
|
|
26
|
+
def initialize(vm:, handler:, invocation:, send_output:, input_queue:, middleware: [],
|
|
27
|
+
outbound_middleware: [])
|
|
27
28
|
@vm = vm
|
|
28
29
|
@handler = handler
|
|
29
30
|
@invocation = invocation
|
|
@@ -32,6 +33,7 @@ module Restate
|
|
|
32
33
|
@run_coros_to_execute = {}
|
|
33
34
|
@attempt_finished_event = AttemptFinishedEvent.new
|
|
34
35
|
@middleware = middleware
|
|
36
|
+
@outbound_middleware = outbound_middleware
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# ── Main entry point ──
|
|
@@ -204,12 +206,14 @@ module Restate
|
|
|
204
206
|
in_serde = resolve_serde(input_serde, handler_meta, :input_serde)
|
|
205
207
|
out_serde = resolve_serde(output_serde, handler_meta, :output_serde)
|
|
206
208
|
parameter = in_serde.serialize(arg)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
with_outbound_middleware(svc_name, handler_name, headers) do |hdrs|
|
|
210
|
+
call_handle = @vm.sys_call(
|
|
211
|
+
service: svc_name, handler: handler_name, parameter: parameter,
|
|
212
|
+
key: key, idempotency_key: idempotency_key, headers: hdrs
|
|
213
|
+
)
|
|
214
|
+
DurableCallFuture.new(self, call_handle.result_handle, call_handle.invocation_id_handle,
|
|
215
|
+
output_serde: out_serde)
|
|
216
|
+
end
|
|
213
217
|
end
|
|
214
218
|
|
|
215
219
|
# Sends a one-way invocation to a Restate service handler (fire-and-forget).
|
|
@@ -219,11 +223,13 @@ module Restate
|
|
|
219
223
|
in_serde = resolve_serde(input_serde, handler_meta, :input_serde)
|
|
220
224
|
parameter = in_serde.serialize(arg)
|
|
221
225
|
delay_ms = delay ? (delay * 1000).to_i : nil
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
with_outbound_middleware(svc_name, handler_name, headers) do |hdrs|
|
|
227
|
+
invocation_id_handle = @vm.sys_send(
|
|
228
|
+
service: svc_name, handler: handler_name, parameter: parameter,
|
|
229
|
+
key: key, delay: delay_ms, idempotency_key: idempotency_key, headers: hdrs
|
|
230
|
+
)
|
|
231
|
+
SendHandle.new(self, invocation_id_handle)
|
|
232
|
+
end
|
|
227
233
|
end
|
|
228
234
|
|
|
229
235
|
# Durably calls a handler on a Restate virtual object, keyed by +key+.
|
|
@@ -233,12 +239,14 @@ module Restate
|
|
|
233
239
|
in_serde = resolve_serde(input_serde, handler_meta, :input_serde)
|
|
234
240
|
out_serde = resolve_serde(output_serde, handler_meta, :output_serde)
|
|
235
241
|
parameter = in_serde.serialize(arg)
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
+
with_outbound_middleware(svc_name, handler_name, headers) do |hdrs|
|
|
243
|
+
call_handle = @vm.sys_call(
|
|
244
|
+
service: svc_name, handler: handler_name, parameter: parameter,
|
|
245
|
+
key: key, idempotency_key: idempotency_key, headers: hdrs
|
|
246
|
+
)
|
|
247
|
+
DurableCallFuture.new(self, call_handle.result_handle, call_handle.invocation_id_handle,
|
|
248
|
+
output_serde: out_serde)
|
|
249
|
+
end
|
|
242
250
|
end
|
|
243
251
|
|
|
244
252
|
# Sends a one-way invocation to a Restate virtual object handler (fire-and-forget).
|
|
@@ -248,11 +256,13 @@ module Restate
|
|
|
248
256
|
in_serde = resolve_serde(input_serde, handler_meta, :input_serde)
|
|
249
257
|
parameter = in_serde.serialize(arg)
|
|
250
258
|
delay_ms = delay ? (delay * 1000).to_i : nil
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
259
|
+
with_outbound_middleware(svc_name, handler_name, headers) do |hdrs|
|
|
260
|
+
invocation_id_handle = @vm.sys_send(
|
|
261
|
+
service: svc_name, handler: handler_name, parameter: parameter,
|
|
262
|
+
key: key, delay: delay_ms, idempotency_key: idempotency_key, headers: hdrs
|
|
263
|
+
)
|
|
264
|
+
SendHandle.new(self, invocation_id_handle)
|
|
265
|
+
end
|
|
256
266
|
end
|
|
257
267
|
|
|
258
268
|
# Durably calls a handler on a Restate workflow, keyed by +key+.
|
|
@@ -332,22 +342,26 @@ module Restate
|
|
|
332
342
|
|
|
333
343
|
# Durably calls a handler using raw bytes (no serialization). Useful for proxying.
|
|
334
344
|
def generic_call(service, handler, arg, key: nil, idempotency_key: nil, headers: nil)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
345
|
+
with_outbound_middleware(service, handler, headers) do |hdrs|
|
|
346
|
+
call_handle = @vm.sys_call(
|
|
347
|
+
service: service, handler: handler, parameter: arg,
|
|
348
|
+
key: key, idempotency_key: idempotency_key, headers: hdrs
|
|
349
|
+
)
|
|
350
|
+
DurableCallFuture.new(self, call_handle.result_handle, call_handle.invocation_id_handle,
|
|
351
|
+
output_serde: nil)
|
|
352
|
+
end
|
|
341
353
|
end
|
|
342
354
|
|
|
343
355
|
# Sends a one-way invocation using raw bytes (no serialization). Useful for proxying.
|
|
344
356
|
def generic_send(service, handler, arg, key: nil, delay: nil, idempotency_key: nil, headers: nil)
|
|
345
357
|
delay_ms = delay ? (delay * 1000).to_i : nil
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
358
|
+
with_outbound_middleware(service, handler, headers) do |hdrs|
|
|
359
|
+
invocation_id_handle = @vm.sys_send(
|
|
360
|
+
service: service, handler: handler, parameter: arg,
|
|
361
|
+
key: key, delay: delay_ms, idempotency_key: idempotency_key, headers: hdrs
|
|
362
|
+
)
|
|
363
|
+
SendHandle.new(self, invocation_id_handle)
|
|
364
|
+
end
|
|
351
365
|
end
|
|
352
366
|
|
|
353
367
|
# ── Request metadata ──
|
|
@@ -453,6 +467,25 @@ module Restate
|
|
|
453
467
|
end
|
|
454
468
|
end
|
|
455
469
|
|
|
470
|
+
# ── Outbound middleware ──
|
|
471
|
+
|
|
472
|
+
# Runs outbound middleware chain (Sidekiq client middleware pattern).
|
|
473
|
+
# Each middleware gets +call(service, handler, headers)+ and must +yield+
|
|
474
|
+
# to continue the chain. The block at the end performs the actual VM call.
|
|
475
|
+
def with_outbound_middleware(service, handler, headers, &action)
|
|
476
|
+
if @outbound_middleware.empty?
|
|
477
|
+
action.call(headers)
|
|
478
|
+
else
|
|
479
|
+
h = headers || {}
|
|
480
|
+
chain = ->(hdrs) { action.call(hdrs) }
|
|
481
|
+
@outbound_middleware.reverse_each do |mw|
|
|
482
|
+
prev = chain
|
|
483
|
+
chain = ->(hdrs) { mw.call(service, handler, hdrs) { prev.call(hdrs) } }
|
|
484
|
+
end
|
|
485
|
+
chain.call(h)
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
456
489
|
# ── Call target resolution ──
|
|
457
490
|
|
|
458
491
|
# Resolves a service+handler pair from class/symbol or string/string.
|
data/lib/restate/version.rb
CHANGED
data/sig/restate.rbs
CHANGED
|
@@ -138,13 +138,19 @@ module Restate
|
|
|
138
138
|
attr_reader identity_keys: Array[String]
|
|
139
139
|
attr_accessor protocol: String?
|
|
140
140
|
attr_reader middleware: Array[untyped]
|
|
141
|
+
attr_reader outbound_middleware: Array[untyped]
|
|
141
142
|
def initialize: () -> void
|
|
142
143
|
def bind: (*untyped svcs) -> self
|
|
143
144
|
def streaming_protocol: () -> self
|
|
144
145
|
def request_response_protocol: () -> self
|
|
145
146
|
def identity_key: (String key) -> self
|
|
146
147
|
def use: (untyped klass, *untyped args, **untyped kwargs) -> self
|
|
148
|
+
def use_outbound: (untyped klass, *untyped args, **untyped kwargs) -> self
|
|
147
149
|
def app: () -> untyped
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def instantiate_middleware: (untyped klass, Array[untyped] args, Hash[Symbol, untyped] kwargs) -> untyped
|
|
148
154
|
end
|
|
149
155
|
|
|
150
156
|
# ── Service proxies ──
|