Class: Restate::CombinedFuture

Inherits:
Object
  • Object
show all
Defined in:
lib/restate/durable_future.rb,
sig/restate.rbs

Overview

A lazy combinator over one or more child futures. The child set can mix DurableFuture leaves (with raw handles) and nested CombinedFuture nodes. The shared-core uses the tree shape to make suspension decisions; nothing blocks until .await is called, so combinators are composable:

Restate.race(Restate.all(a, b), c).await

Supported variants (mirroring UnresolvedFuture in restate-sdk-shared-core):

:first_completed                 → JS Promise.race
:all_succeeded_or_first_failed   → JS Promise.all
:first_succeeded_or_all_failed   → JS Promise.any
:all_completed                   → JS Promise.allSettled

Constant Summary collapse

VALID_VARIANTS =

Returns:

  • (Array[Symbol])
%i[
  first_completed
  all_succeeded_or_first_failed
  first_succeeded_or_all_failed
  all_completed
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(ctx, variant, children) ⇒ CombinedFuture

Returns a new instance of CombinedFuture.

Parameters:

  • ctx (Object)
  • variant (Symbol)
  • children (Array[untyped])

Raises:

  • (ArgumentError)


138
139
140
141
142
143
144
145
146
147
# File 'lib/restate/durable_future.rb', line 138

def initialize(ctx, variant, children)
  raise ArgumentError, "unknown combinator variant: #{variant}" unless VALID_VARIANTS.include?(variant)

  @ctx = ctx
  @variant = variant
  @children = children
  @resolved = false
  @value = nil
  @error = nil
end

Instance Method Details

#awaitObject

Block until this combinator settles per its variant. Caches results (including failures) across calls.

Returns:

  • (Object)

Raises:

  • (@error)


157
158
159
160
161
162
# File 'lib/restate/durable_future.rb', line 157

def await
  resolve_combined! unless @resolved
  raise @error if @error

  @value
end

#completed?Boolean

Non-blocking introspection. True iff calling .await is guaranteed not to block. Conservative for the variants that allow early-completion on failure (+:all_succeeded_or_first_failed+, :first_succeeded_or_all_failed) — we report false until every child is settled, because checking failure status of a leaf would require consuming its notification.

Returns:

  • (Boolean)


169
170
171
172
173
174
175
176
177
178
# File 'lib/restate/durable_future.rb', line 169

def completed?
  return true if @resolved

  case @variant
  when :first_completed
    @children.any?(&:completed?)
  else
    @children.all?(&:completed?)
  end
end

#finalize_all_completedArray[Hash[Symbol, untyped]]

Returns:

  • (Array[Hash[Symbol, untyped]])


212
213
214
215
216
217
218
# File 'lib/restate/durable_future.rb', line 212

def finalize_all_completed
  @children.map do |c|
    { status: :fulfilled, value: c.await }
  rescue TerminalError => e
    { status: :rejected, reason: e }
  end
end

#finalize_all_succeededArray[untyped]

Returns:

  • (Array[untyped])


204
205
206
207
208
209
210
# File 'lib/restate/durable_future.rb', line 204

def finalize_all_succeeded
  # Surface any settled-and-failed child first (short-circuit). After this
  # scan, if no failure was raised, every child must be settled-and-success
  # per AllSucceededOrFirstFailed semantics.
  @children.each { |c| c.await if c.completed? }
  @children.map(&:await)
end

#finalize_first_completedObject

Returns:

  • (Object)


200
201
202
# File 'lib/restate/durable_future.rb', line 200

def finalize_first_completed
  @children.find(&:completed?).await
end

#finalize_first_succeededObject

Returns:

  • (Object)

Raises:



220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/restate/durable_future.rb', line 220

def finalize_first_succeeded
  errors = [] # : Array[TerminalError]
  @children.each do |c|
    next unless c.completed?

    return c.await
  rescue TerminalError => e
    errors << e
  end
  raise TerminalError.new("all futures failed: #{errors.map(&:message).join('; ')}",
                          status_code: 500)
end

#finalize_valueObject

Returns:

  • (Object)


191
192
193
194
195
196
197
198
# File 'lib/restate/durable_future.rb', line 191

def finalize_value
  case @variant
  when :first_completed then finalize_first_completed
  when :all_succeeded_or_first_failed then finalize_all_succeeded
  when :all_completed then finalize_all_completed
  when :first_succeeded_or_all_failed then finalize_first_succeeded
  end
end

#resolve_combined!void

This method returns an undefined value.



182
183
184
185
186
187
188
189
# File 'lib/restate/durable_future.rb', line 182

def resolve_combined!
  @ctx.wait_combined(tree)
  @value = finalize_value
rescue TerminalError => e
  @error = e
ensure
  @resolved = true
end

#treeObject

Recursive tree representation the native do_await binding consumes. Leaves are integer handles; inner nodes are [variant, [child...]] pairs.

Returns:

  • (Object)


151
152
153
# File 'lib/restate/durable_future.rb', line 151

def tree
  [@variant, @children.map { |c| c.is_a?(CombinedFuture) ? c.tree : c.handle }]
end