MCPcopy Create free account
hub / github.com/erpc/erpc / executeConsensus

Method executeConsensus

consensus/executor.go:180–291  ·  view source on GitHub ↗

executeConsensus decouples two distinct concerns: 1. Caller-visible latency: the caller must return promptly when its context is cancelled (HTTP disconnect, upstream deadline, shutdown). 2. Analysis completeness: misbehavior tracking and metrics must see every participant's response, even ones that

(
	ctx context.Context,
	lg *zerolog.Logger,
	originalReq *common.NormalizedRequest,
	labels metricsLabels,
	in inner,
	startTime time.Time,
	consensusSpan trace.Span,
)

Source from the content-addressed store, hash-verified

178// which is already bounded by failsafe policy timeouts and HTTP client
179// timeouts — no new magic-number budget is required.
180func (e *executor) executeConsensus(
181 ctx context.Context,
182 lg *zerolog.Logger,
183 originalReq *common.NormalizedRequest,
184 labels metricsLabels,
185 in inner,
186 startTime time.Time,
187 consensusSpan trace.Span,
188) *slotResult {
189 ctx, collectionSpan := common.StartDetailSpan(ctx, "Consensus.CollectResponses")
190 // NOTE: collectionSpan.End() is owned by runAnalyzer (in its deferred
191 // cleanup), not this function. The analyzer outlives executeConsensus on
192 // the caller-cancel path, and ending the span here would drop late
193 // attributes (short_circuited, responses.collected).
194
195 // For fire-and-forget mode, detach from parent context cancellation so background
196 // requests continue even after the HTTP response is sent. This is critical for
197 // transaction broadcasting where we want all nodes to receive the transaction.
198 // For normal mode, inherit parent cancellation for proper resource cleanup.
199 var baseCtx context.Context
200 if e.config.fireAndForget {
201 baseCtx = context.WithoutCancel(ctx)
202 } else {
203 baseCtx = ctx
204 }
205
206 cancellableCtx, cancelFunc := context.WithCancel(baseCtx)
207 var cancelOnce sync.Once
208 cancelRemaining := func() {
209 cancelOnce.Do(cancelFunc)
210 }
211
212 // Cancel remaining requests on exit, unless fire-and-forget mode is enabled.
213 // In fire-and-forget mode we want background requests to complete naturally.
214 // sync.Once ensures cancel is called at most once even if called explicitly earlier.
215 defer func() {
216 if !e.config.fireAndForget {
217 cancelRemaining()
218 }
219 }()
220
221 // Spawn only as many participants as configured by policy
222 maxToSpawn := e.maxParticipants
223 if maxToSpawn <= 0 {
224 maxToSpawn = 1
225 }
226 // Record on the request's ExecState that THIS request went through
227 // the consensus executor and how many participants we spawned. Read
228 // downstream by diagnostic surfaces (admin endpoints, response
229 // headers, the simulator's lifecycle drawer) so operators can tell
230 // "this request was a consensus race" from "this was a hedge race"
231 // — the two look similar in the per-attempt log otherwise.
232 if st := originalReq.ExecState(); st != nil {
233 st.ConsensusSlots.Add(int32(maxToSpawn))
234 }
235 responseChan := make(chan *execResult, maxToSpawn)
236 // Per-slot cancellable child contexts let us cancel losers explicitly.
237 // Each slot inherits the shared cancellableCtx (which is cancelled

Callers 1

RunMethod · 0.95

Calls 8

executeParticipantMethod · 0.95
runAnalyzerMethod · 0.95
drainAbandonedOutcomeMethod · 0.95
handleCallerAbandonedMethod · 0.95
StartDetailSpanFunction · 0.92
ExecStateMethod · 0.80
AddMethod · 0.65

Tested by

no test coverage detected