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.
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'async'
@@ -18,35 +18,25 @@ module Restate
18
18
  class ServerContext
19
19
  include WorkflowContext
20
20
  include WorkflowSharedContext
21
- extend T::Sig
22
21
 
23
- LOGGER = T.let(Logger.new($stdout, progname: 'Restate::ServerContext'), Logger)
22
+ LOGGER = Logger.new($stdout, progname: 'Restate::ServerContext')
24
23
 
25
- sig { returns(VMWrapper) }
26
- attr_reader :vm
24
+ attr_reader :vm, :invocation
27
25
 
28
- sig { returns(T.untyped) }
29
- attr_reader :invocation
30
-
31
- sig do
32
- params(vm: VMWrapper, handler: T.untyped, invocation: T.untyped, send_output: T.untyped,
33
- input_queue: Async::Queue, middleware: T::Array[T.untyped]).void
34
- end
35
26
  def initialize(vm:, handler:, invocation:, send_output:, input_queue:, middleware: [])
36
- @vm = T.let(vm, VMWrapper)
37
- @handler = T.let(handler, T.untyped)
38
- @invocation = T.let(invocation, T.untyped)
39
- @send_output = T.let(send_output, T.untyped)
40
- @input_queue = T.let(input_queue, Async::Queue)
41
- @run_coros_to_execute = T.let({}, T::Hash[Integer, T.untyped])
42
- @attempt_finished_event = T.let(AttemptFinishedEvent.new, AttemptFinishedEvent)
43
- @middleware = T.let(middleware, T::Array[T.untyped])
27
+ @vm = vm
28
+ @handler = handler
29
+ @invocation = invocation
30
+ @send_output = send_output
31
+ @input_queue = input_queue
32
+ @run_coros_to_execute = {}
33
+ @attempt_finished_event = AttemptFinishedEvent.new
34
+ @middleware = middleware
44
35
  end
45
36
 
46
37
  # ── Main entry point ──
47
38
 
48
39
  # Runs the handler to completion, writing the output (or failure) to the journal.
49
- sig { void }
50
40
  def enter
51
41
  Thread.current[:restate_context] = self
52
42
  Thread.current[:restate_service_kind] = @handler.service_tag.kind
@@ -54,7 +44,7 @@ module Restate
54
44
  in_buffer = @invocation.input_buffer
55
45
  out_buffer = Restate.invoke_handler(handler: @handler, ctx: self, in_buffer: in_buffer,
56
46
  middleware: @middleware)
57
- @vm.sys_write_output_success(out_buffer.b)
47
+ @vm.sys_write_output_success(out_buffer)
58
48
  @vm.sys_end
59
49
  rescue TerminalError => e
60
50
  failure = Failure.new(code: e.status_code, message: e.message)
@@ -66,8 +56,8 @@ module Restate
66
56
  raise
67
57
  rescue StandardError => e
68
58
  # Walk the cause chain for TerminalError or internal exceptions
69
- cause = T.let(e, T.nilable(Exception))
70
- handled = T.let(false, T::Boolean)
59
+ cause = e
60
+ handled = false
71
61
  while cause
72
62
  if cause.is_a?(TerminalError)
73
63
  f = Failure.new(code: cause.status_code, message: cause.message)
@@ -95,7 +85,6 @@ module Restate
95
85
  # Called by the server when the attempt ends (handler completed, disconnected,
96
86
  # or transient error). Signals the attempt_finished_event so that user code
97
87
  # and background pool jobs can clean up.
98
- sig { void }
99
88
  def on_attempt_finished
100
89
  @attempt_finished_event.set!
101
90
  end
@@ -103,44 +92,37 @@ module Restate
103
92
  # ── State operations ──
104
93
 
105
94
  # Durably retrieves a state entry by name. Returns nil if unset.
106
- sig { override.params(name: String, serde: T.untyped).returns(T.untyped) }
107
95
  def get(name, serde: JsonSerde)
108
96
  get_async(name, serde: serde).await
109
97
  end
110
98
 
111
99
  # Returns a DurableFuture for a state entry. Resolves to nil if unset.
112
- sig { override.params(name: String, serde: T.untyped).returns(DurableFuture) }
113
100
  def get_async(name, serde: JsonSerde)
114
101
  handle = @vm.sys_get_state(name)
115
102
  DurableFuture.new(self, handle, serde: serde)
116
103
  end
117
104
 
118
105
  # Durably sets a state entry. The value is serialized via +serde+.
119
- sig { override.params(name: String, value: T.untyped, serde: T.untyped).void }
120
106
  def set(name, value, serde: JsonSerde)
121
- @vm.sys_set_state(name, serde.serialize(value).b)
107
+ @vm.sys_set_state(name, serde.serialize(value))
122
108
  end
123
109
 
124
110
  # Durably removes a single state entry by name.
125
- sig { override.params(name: String).void }
126
111
  def clear(name)
127
112
  @vm.sys_clear_state(name)
128
113
  end
129
114
 
130
115
  # Durably removes all state entries for this virtual object or workflow.
131
- sig { override.void }
132
116
  def clear_all
133
117
  @vm.sys_clear_all_state
134
118
  end
135
119
 
136
120
  # Returns the list of all state entry names for this virtual object or workflow.
137
- sig { override.returns(T.untyped) }
138
121
  def state_keys
139
122
  state_keys_async.await
140
123
  end
141
124
 
142
125
  # Returns a DurableFuture for the list of all state entry names.
143
- sig { override.returns(DurableFuture) }
144
126
  def state_keys_async
145
127
  handle = @vm.sys_get_state_keys
146
128
  DurableFuture.new(self, handle)
@@ -150,7 +132,6 @@ module Restate
150
132
 
151
133
  # Returns a durable future that completes after the given duration.
152
134
  # The timer survives handler restarts.
153
- sig { params(seconds: Numeric).returns(DurableFuture) }
154
135
  def sleep(seconds)
155
136
  millis = (seconds * 1000).to_i
156
137
  handle = @vm.sys_sleep(millis)
@@ -158,45 +139,31 @@ module Restate
158
139
  end
159
140
 
160
141
  # Block until a previously created handle completes. Returns the value.
161
- sig { params(handle: Integer).returns(T.untyped) }
162
142
  def resolve_handle(handle)
163
143
  poll_and_take(handle)
164
144
  end
165
145
 
166
146
  # Wait until any of the given handles completes. Does not take notifications.
167
- sig { params(handles: T::Array[Integer]).void }
168
147
  def wait_any_handle(handles)
169
148
  poll_or_cancel(handles) unless handles.any? { |h| @vm.is_completed(h) }
170
149
  end
171
150
 
172
151
  # Check if a handle is completed (non-blocking).
173
- sig { params(handle: Integer).returns(T::Boolean) }
174
152
  def completed?(handle)
175
153
  @vm.is_completed(handle)
176
154
  end
177
155
 
178
156
  # Take a completed handle's notification, returning the value.
179
157
  # Raises TerminalError if the handle resolved to a failure.
180
- sig { params(handle: Integer).returns(T.untyped) }
181
158
  def take_completed(handle)
182
159
  must_take_notification(handle)
183
160
  end
184
161
 
185
162
  # Wait until any of the given futures completes. Returns [completed, remaining].
186
- sig { override.params(futures: DurableFuture).returns([T::Array[DurableFuture], T::Array[DurableFuture]]) }
187
163
  def wait_any(*futures)
188
164
  handles = futures.map(&:handle)
189
165
  wait_any_handle(handles)
190
- completed = []
191
- remaining = []
192
- futures.each do |f|
193
- if f.completed?
194
- completed << f
195
- else
196
- remaining << f
197
- end
198
- end
199
- [completed, remaining]
166
+ futures.partition(&:completed?)
200
167
  end
201
168
 
202
169
  # ── Durable run (side effect) ──
@@ -207,20 +174,15 @@ module Restate
207
174
  # Pass +background: true+ to run the block in a real OS Thread, keeping the
208
175
  # fiber event loop responsive for other concurrent handlers. Use this for
209
176
  # CPU-intensive work.
210
- sig do
211
- override.params(
212
- name: String,
213
- serde: T.untyped,
214
- retry_policy: T.nilable(RunRetryPolicy),
215
- background: T::Boolean,
216
- action: T.proc.returns(T.untyped)
217
- ).returns(DurableFuture)
218
- end
219
177
  def run(name, serde: JsonSerde, retry_policy: nil, background: false, &action)
220
178
  handle = @vm.sys_run(name)
221
179
 
222
- executor = background ? :execute_run_threaded : :execute_run
223
- @run_coros_to_execute[handle] = -> { send(executor, handle, action, serde, retry_policy) }
180
+ @run_coros_to_execute[handle] =
181
+ if background
182
+ -> { execute_run_threaded(handle, action, serde, retry_policy) }
183
+ else
184
+ -> { execute_run(handle, action, serde, retry_policy) }
185
+ end
224
186
 
225
187
  DurableFuture.new(self, handle, serde: serde)
226
188
  end
@@ -229,15 +191,6 @@ module Restate
229
191
  # and returns the result directly.
230
192
  #
231
193
  # Accepts all the same options as +run+, including +background: true+.
232
- sig do
233
- override.params(
234
- name: String,
235
- serde: T.untyped,
236
- retry_policy: T.nilable(RunRetryPolicy),
237
- background: T::Boolean,
238
- action: T.proc.returns(T.untyped)
239
- ).returns(T.untyped)
240
- end
241
194
  def run_sync(name, serde: JsonSerde, retry_policy: nil, background: false, &action)
242
195
  run(name, serde: serde, retry_policy: retry_policy, background: background, &action).await
243
196
  end
@@ -245,18 +198,6 @@ module Restate
245
198
  # ── Service calls ──
246
199
 
247
200
  # Durably calls a handler on a Restate service and returns a future for its result.
248
- sig do
249
- override.params(
250
- service: T.any(String, T::Class[T.anything]),
251
- handler: T.any(String, Symbol),
252
- arg: T.untyped,
253
- key: T.nilable(String),
254
- idempotency_key: T.nilable(String),
255
- headers: T.nilable(T::Hash[String, String]),
256
- input_serde: T.untyped,
257
- output_serde: T.untyped
258
- ).returns(DurableCallFuture)
259
- end
260
201
  def service_call(service, handler, arg, key: nil, idempotency_key: nil, headers: nil,
261
202
  input_serde: NOT_SET, output_serde: NOT_SET)
262
203
  svc_name, handler_name, handler_meta = resolve_call_target(service, handler)
@@ -264,7 +205,7 @@ module Restate
264
205
  out_serde = resolve_serde(output_serde, handler_meta, :output_serde)
265
206
  parameter = in_serde.serialize(arg)
266
207
  call_handle = @vm.sys_call(
267
- service: svc_name, handler: handler_name, parameter: parameter.b,
208
+ service: svc_name, handler: handler_name, parameter: parameter,
268
209
  key: key, idempotency_key: idempotency_key, headers: headers
269
210
  )
270
211
  DurableCallFuture.new(self, call_handle.result_handle, call_handle.invocation_id_handle,
@@ -272,18 +213,6 @@ module Restate
272
213
  end
273
214
 
274
215
  # Sends a one-way invocation to a Restate service handler (fire-and-forget).
275
- sig do
276
- override.params(
277
- service: T.any(String, T::Class[T.anything]),
278
- handler: T.any(String, Symbol),
279
- arg: T.untyped,
280
- key: T.nilable(String),
281
- delay: T.nilable(Numeric),
282
- idempotency_key: T.nilable(String),
283
- headers: T.nilable(T::Hash[String, String]),
284
- input_serde: T.untyped
285
- ).returns(SendHandle)
286
- end
287
216
  def service_send(service, handler, arg, key: nil, delay: nil, idempotency_key: nil, headers: nil,
288
217
  input_serde: NOT_SET)
289
218
  svc_name, handler_name, handler_meta = resolve_call_target(service, handler)
@@ -291,25 +220,13 @@ module Restate
291
220
  parameter = in_serde.serialize(arg)
292
221
  delay_ms = delay ? (delay * 1000).to_i : nil
293
222
  invocation_id_handle = @vm.sys_send(
294
- service: svc_name, handler: handler_name, parameter: parameter.b,
223
+ service: svc_name, handler: handler_name, parameter: parameter,
295
224
  key: key, delay: delay_ms, idempotency_key: idempotency_key, headers: headers
296
225
  )
297
226
  SendHandle.new(self, invocation_id_handle)
298
227
  end
299
228
 
300
229
  # Durably calls a handler on a Restate virtual object, keyed by +key+.
301
- sig do
302
- override.params(
303
- service: T.any(String, T::Class[T.anything]),
304
- handler: T.any(String, Symbol),
305
- key: String,
306
- arg: T.untyped,
307
- idempotency_key: T.nilable(String),
308
- headers: T.nilable(T::Hash[String, String]),
309
- input_serde: T.untyped,
310
- output_serde: T.untyped
311
- ).returns(DurableCallFuture)
312
- end
313
230
  def object_call(service, handler, key, arg, idempotency_key: nil, headers: nil,
314
231
  input_serde: NOT_SET, output_serde: NOT_SET)
315
232
  svc_name, handler_name, handler_meta = resolve_call_target(service, handler)
@@ -317,7 +234,7 @@ module Restate
317
234
  out_serde = resolve_serde(output_serde, handler_meta, :output_serde)
318
235
  parameter = in_serde.serialize(arg)
319
236
  call_handle = @vm.sys_call(
320
- service: svc_name, handler: handler_name, parameter: parameter.b,
237
+ service: svc_name, handler: handler_name, parameter: parameter,
321
238
  key: key, idempotency_key: idempotency_key, headers: headers
322
239
  )
323
240
  DurableCallFuture.new(self, call_handle.result_handle, call_handle.invocation_id_handle,
@@ -325,18 +242,6 @@ module Restate
325
242
  end
326
243
 
327
244
  # Sends a one-way invocation to a Restate virtual object handler (fire-and-forget).
328
- sig do
329
- override.params(
330
- service: T.any(String, T::Class[T.anything]),
331
- handler: T.any(String, Symbol),
332
- key: String,
333
- arg: T.untyped,
334
- delay: T.nilable(Numeric),
335
- idempotency_key: T.nilable(String),
336
- headers: T.nilable(T::Hash[String, String]),
337
- input_serde: T.untyped
338
- ).returns(SendHandle)
339
- end
340
245
  def object_send(service, handler, key, arg, delay: nil, idempotency_key: nil, headers: nil,
341
246
  input_serde: NOT_SET)
342
247
  svc_name, handler_name, handler_meta = resolve_call_target(service, handler)
@@ -344,25 +249,13 @@ module Restate
344
249
  parameter = in_serde.serialize(arg)
345
250
  delay_ms = delay ? (delay * 1000).to_i : nil
346
251
  invocation_id_handle = @vm.sys_send(
347
- service: svc_name, handler: handler_name, parameter: parameter.b,
252
+ service: svc_name, handler: handler_name, parameter: parameter,
348
253
  key: key, delay: delay_ms, idempotency_key: idempotency_key, headers: headers
349
254
  )
350
255
  SendHandle.new(self, invocation_id_handle)
351
256
  end
352
257
 
353
258
  # Durably calls a handler on a Restate workflow, keyed by +key+.
354
- sig do
355
- override.params(
356
- service: T.any(String, T::Class[T.anything]),
357
- handler: T.any(String, Symbol),
358
- key: String,
359
- arg: T.untyped,
360
- idempotency_key: T.nilable(String),
361
- headers: T.nilable(T::Hash[String, String]),
362
- input_serde: T.untyped,
363
- output_serde: T.untyped
364
- ).returns(DurableCallFuture)
365
- end
366
259
  def workflow_call(service, handler, key, arg, idempotency_key: nil, headers: nil,
367
260
  input_serde: NOT_SET, output_serde: NOT_SET)
368
261
  object_call(service, handler, key, arg, idempotency_key: idempotency_key, headers: headers,
@@ -370,18 +263,6 @@ module Restate
370
263
  end
371
264
 
372
265
  # Sends a one-way invocation to a Restate workflow handler (fire-and-forget).
373
- sig do
374
- override.params(
375
- service: T.any(String, T::Class[T.anything]),
376
- handler: T.any(String, Symbol),
377
- key: String,
378
- arg: T.untyped,
379
- delay: T.nilable(Numeric),
380
- idempotency_key: T.nilable(String),
381
- headers: T.nilable(T::Hash[String, String]),
382
- input_serde: T.untyped
383
- ).returns(SendHandle)
384
- end
385
266
  def workflow_send(service, handler, key, arg, delay: nil, idempotency_key: nil, headers: nil,
386
267
  input_serde: NOT_SET)
387
268
  object_send(service, handler, key, arg, delay: delay, idempotency_key: idempotency_key, headers: headers,
@@ -391,20 +272,17 @@ module Restate
391
272
  # ── Awakeables ──
392
273
 
393
274
  # Creates an awakeable and returns [awakeable_id, DurableFuture].
394
- sig { override.params(serde: T.untyped).returns([String, DurableFuture]) }
395
275
  def awakeable(serde: JsonSerde)
396
276
  id, handle = @vm.sys_awakeable
397
277
  [id, DurableFuture.new(self, handle, serde: serde)]
398
278
  end
399
279
 
400
280
  # Resolves an awakeable with a success value.
401
- sig { override.params(awakeable_id: String, payload: T.untyped, serde: T.untyped).void }
402
281
  def resolve_awakeable(awakeable_id, payload, serde: JsonSerde)
403
- @vm.sys_complete_awakeable_success(awakeable_id, serde.serialize(payload).b)
282
+ @vm.sys_complete_awakeable_success(awakeable_id, serde.serialize(payload))
404
283
  end
405
284
 
406
285
  # Rejects an awakeable with a terminal failure.
407
- sig { override.params(awakeable_id: String, message: String, code: Integer).void }
408
286
  def reject_awakeable(awakeable_id, message, code: 500)
409
287
  failure = Failure.new(code: code, message: message)
410
288
  @vm.sys_complete_awakeable_failure(awakeable_id, failure)
@@ -413,7 +291,6 @@ module Restate
413
291
  # ── Promises (Workflow API) ──
414
292
 
415
293
  # Gets a durable promise value, blocking until resolved.
416
- sig { override.params(name: String, serde: T.untyped).returns(T.untyped) }
417
294
  def promise(name, serde: JsonSerde)
418
295
  handle = @vm.sys_get_promise(name)
419
296
  poll_and_take(handle) do |raw|
@@ -422,7 +299,6 @@ module Restate
422
299
  end
423
300
 
424
301
  # Peeks at a durable promise value without blocking. Returns nil if not yet resolved.
425
- sig { override.params(name: String, serde: T.untyped).returns(T.untyped) }
426
302
  def peek_promise(name, serde: JsonSerde)
427
303
  handle = @vm.sys_peek_promise(name)
428
304
  poll_and_take(handle) do |raw|
@@ -431,15 +307,13 @@ module Restate
431
307
  end
432
308
 
433
309
  # Resolves a durable promise with a success value.
434
- sig { override.params(name: String, payload: T.untyped, serde: T.untyped).void }
435
310
  def resolve_promise(name, payload, serde: JsonSerde)
436
- handle = @vm.sys_complete_promise_success(name, serde.serialize(payload).b)
311
+ handle = @vm.sys_complete_promise_success(name, serde.serialize(payload))
437
312
  poll_and_take(handle)
438
313
  nil
439
314
  end
440
315
 
441
316
  # Rejects a durable promise with a terminal failure.
442
- sig { override.params(name: String, message: String, code: Integer).void }
443
317
  def reject_promise(name, message, code: 500)
444
318
  failure = Failure.new(code: code, message: message)
445
319
  handle = @vm.sys_complete_promise_failure(name, failure)
@@ -450,7 +324,6 @@ module Restate
450
324
  # ── Cancel invocation ──
451
325
 
452
326
  # Requests cancellation of another invocation by its id.
453
- sig { override.params(invocation_id: String).void }
454
327
  def cancel_invocation(invocation_id)
455
328
  @vm.sys_cancel_invocation(invocation_id)
456
329
  end
@@ -458,19 +331,9 @@ module Restate
458
331
  # ── Generic calls (raw bytes, no serde) ──
459
332
 
460
333
  # Durably calls a handler using raw bytes (no serialization). Useful for proxying.
461
- sig do
462
- override.params(
463
- service: String,
464
- handler: String,
465
- arg: String,
466
- key: T.nilable(String),
467
- idempotency_key: T.nilable(String),
468
- headers: T.nilable(T::Hash[String, String])
469
- ).returns(DurableCallFuture)
470
- end
471
334
  def generic_call(service, handler, arg, key: nil, idempotency_key: nil, headers: nil)
472
335
  call_handle = @vm.sys_call(
473
- service: service, handler: handler, parameter: arg.b,
336
+ service: service, handler: handler, parameter: arg,
474
337
  key: key, idempotency_key: idempotency_key, headers: headers
475
338
  )
476
339
  DurableCallFuture.new(self, call_handle.result_handle, call_handle.invocation_id_handle,
@@ -478,21 +341,10 @@ module Restate
478
341
  end
479
342
 
480
343
  # Sends a one-way invocation using raw bytes (no serialization). Useful for proxying.
481
- sig do
482
- override.params(
483
- service: String,
484
- handler: String,
485
- arg: String,
486
- key: T.nilable(String),
487
- delay: T.nilable(Numeric),
488
- idempotency_key: T.nilable(String),
489
- headers: T.nilable(T::Hash[String, String])
490
- ).returns(SendHandle)
491
- end
492
344
  def generic_send(service, handler, arg, key: nil, delay: nil, idempotency_key: nil, headers: nil)
493
345
  delay_ms = delay ? (delay * 1000).to_i : nil
494
346
  invocation_id_handle = @vm.sys_send(
495
- service: service, handler: handler, parameter: arg.b,
347
+ service: service, handler: handler, parameter: arg,
496
348
  key: key, delay: delay_ms, idempotency_key: idempotency_key, headers: headers
497
349
  )
498
350
  SendHandle.new(self, invocation_id_handle)
@@ -501,7 +353,6 @@ module Restate
501
353
  # ── Request metadata ──
502
354
 
503
355
  # Returns metadata about the current invocation (id, headers, raw body).
504
- sig { override.returns(T.untyped) }
505
356
  def request
506
357
  @request ||= Request.new(
507
358
  id: @invocation.invocation_id,
@@ -512,7 +363,6 @@ module Restate
512
363
  end
513
364
 
514
365
  # Returns the key for this virtual object or workflow invocation.
515
- sig { override.returns(String) }
516
366
  def key
517
367
  @invocation.key
518
368
  end
@@ -522,18 +372,11 @@ module Restate
522
372
  # ── Progress loop ──
523
373
 
524
374
  # Polls until the given handle(s) complete, then takes the notification.
525
- sig do
526
- params(
527
- handle: Integer,
528
- block: T.nilable(T.proc.params(arg0: T.untyped).returns(T.untyped))
529
- ).returns(T.untyped)
530
- end
531
- def poll_and_take(handle, &block)
375
+ def poll_and_take(handle, &)
532
376
  poll_or_cancel([handle]) unless @vm.is_completed(handle)
533
- must_take_notification(handle, &block)
377
+ must_take_notification(handle, &)
534
378
  end
535
379
 
536
- sig { params(handles: T::Array[Integer]).void }
537
380
  def poll_or_cancel(handles)
538
381
  loop do
539
382
  flush_output
@@ -579,12 +422,6 @@ module Restate
579
422
  end
580
423
  end
581
424
 
582
- sig do
583
- params(
584
- handle: Integer,
585
- block: T.nilable(T.proc.params(arg0: T.untyped).returns(T.untyped))
586
- ).returns(T.untyped)
587
- end
588
425
  def must_take_notification(handle, &block)
589
426
  result = @vm.take_notification(handle)
590
427
 
@@ -607,7 +444,6 @@ module Restate
607
444
  end
608
445
  end
609
446
 
610
- sig { void }
611
447
  def flush_output
612
448
  loop do
613
449
  output = @vm.take_output
@@ -621,78 +457,49 @@ module Restate
621
457
 
622
458
  # Resolves a service+handler pair from class/symbol or string/string.
623
459
  # Returns [service_name, handler_name, handler_metadata_or_nil].
624
- sig do
625
- params(
626
- service: T.any(String, T::Class[T.anything]),
627
- handler: T.any(String, Symbol)
628
- ).returns([String, String, T.nilable(Handler)])
629
- end
630
460
  def resolve_call_target(service, handler)
461
+ handler_name = handler.is_a?(Symbol) ? handler.name : handler.to_s
631
462
  if service.is_a?(Class) && service.respond_to?(:service_name)
632
- svc_name = T.unsafe(service).service_name
633
- handler_name = handler.to_s
634
- handler_meta = service.respond_to?(:handlers) ? T.unsafe(service).handlers[handler_name] : nil
463
+ svc_name = service.service_name
464
+ handler_meta = service.respond_to?(:handlers) ? service.handlers[handler_name] : nil
635
465
  [svc_name, handler_name, handler_meta]
636
466
  else
637
- [service.to_s, handler.to_s, nil]
467
+ [service.to_s, handler_name, nil]
638
468
  end
639
469
  end
640
470
 
641
471
  # Resolves a serde value: if the caller passed NOT_SET, fall back to handler metadata, then JsonSerde.
642
- sig { params(caller_serde: T.untyped, handler_meta: T.nilable(Handler), field: Symbol).returns(T.untyped) }
643
472
  def resolve_serde(caller_serde, handler_meta, field)
644
473
  return caller_serde unless caller_serde.equal?(NOT_SET)
474
+ return JsonSerde unless handler_meta
645
475
 
646
- if handler_meta
647
- handler_meta.handler_io.public_send(field)
648
- else
649
- JsonSerde
476
+ io = handler_meta.handler_io
477
+ case field
478
+ when :input_serde then io.input_serde
479
+ when :output_serde then io.output_serde
480
+ else JsonSerde
650
481
  end
651
482
  end
652
483
 
653
484
  # ── Run execution ──
654
485
 
655
- sig do
656
- params(
657
- handle: Integer,
658
- action: T.proc.returns(T.untyped),
659
- serde: T.untyped,
660
- retry_policy: T.nilable(RunRetryPolicy)
661
- ).void
662
- end
663
486
  def execute_run(handle, action, serde, retry_policy)
664
487
  propose_run_result(handle, action, serde, retry_policy)
665
488
  end
666
489
 
667
490
  # Like execute_run, but offloads the action to a real OS Thread.
668
491
  # The fiber yields (via IO.pipe) while the thread runs, keeping the event loop responsive.
669
- sig do
670
- params(
671
- handle: Integer,
672
- action: T.proc.returns(T.untyped),
673
- serde: T.untyped,
674
- retry_policy: T.nilable(RunRetryPolicy)
675
- ).void
676
- end
677
492
  def execute_run_threaded(handle, action, serde, retry_policy)
678
493
  propose_run_result(handle, -> { offload_to_thread(action) }, serde, retry_policy)
679
494
  end
680
495
 
681
496
  # Runs the action and proposes the result (success/failure/transient) to the VM.
682
- sig do
683
- params(
684
- handle: Integer,
685
- action: T.proc.returns(T.untyped),
686
- serde: T.untyped,
687
- retry_policy: T.nilable(RunRetryPolicy)
688
- ).void
689
- end
690
497
  def propose_run_result(handle, action, serde, retry_policy)
691
498
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
692
499
  begin
693
500
  result = action.call
694
501
  buffer = serde.serialize(result)
695
- @vm.propose_run_completion_success(handle, buffer.b)
502
+ @vm.propose_run_completion_success(handle, buffer)
696
503
  rescue TerminalError => e
697
504
  failure = Failure.new(code: e.status_code, message: e.message)
698
505
  @vm.propose_run_completion_failure(handle, failure)
@@ -734,11 +541,10 @@ module Restate
734
541
  # most blocking I/O (Net::HTTP, TCPSocket, etc.) and yields the fiber
735
542
  # automatically. +background: true+ is only needed for CPU-heavy native
736
543
  # extensions that release the GVL (e.g., image processing, crypto).
737
- sig { params(action: T.proc.returns(T.untyped)).returns(T.untyped) }
738
544
  def offload_to_thread(action)
739
545
  read_io, write_io = IO.pipe
740
- result = T.let(nil, T.untyped)
741
- error = T.let(nil, T.nilable(Exception))
546
+ result = nil
547
+ error = nil
742
548
  event = @attempt_finished_event
743
549
 
744
550
  begin
@@ -772,25 +578,21 @@ module Restate
772
578
  # Avoids creating a new Thread per call (~1ms + ~1MB stack each).
773
579
  # Workers are daemon threads that do not prevent process exit.
774
580
  module BackgroundPool
775
- extend T::Sig
776
-
777
- @queue = T.let(Queue.new, Queue)
778
- @workers = T.let([], T::Array[Thread])
779
- @mutex = T.let(Mutex.new, Mutex)
780
- @size = T.let(0, Integer)
581
+ @queue = Queue.new
582
+ @workers = []
583
+ @mutex = Mutex.new
584
+ @size = 0
781
585
 
782
- POOL_SIZE = T.let(Integer(ENV.fetch('RESTATE_BACKGROUND_POOL_SIZE', 8)), Integer)
586
+ POOL_SIZE = Integer(ENV.fetch('RESTATE_BACKGROUND_POOL_SIZE', 8))
783
587
 
784
588
  module_function
785
589
 
786
590
  # Submit a block to be executed by a pool worker.
787
- sig { params(block: T.proc.void).void }
788
591
  def submit(&block)
789
592
  ensure_started
790
593
  @queue.push(block)
791
594
  end
792
595
 
793
- sig { void }
794
596
  def ensure_started
795
597
  return if @size >= POOL_SIZE
796
598