feat: serialize keyed tasks across queues with durable fencing tokens

This commit is contained in:
2026-09-17 18:25:00 +08:00
parent 993786476e
commit 958b4a8cea
43 changed files with 1319 additions and 82 deletions

View File

@@ -50,12 +50,13 @@ func (s *ExecutionStore) transaction(ctx context.Context, token auth.Token, acti
}
type sessionState struct {
queue core.ID
capacity uint32
sequence uint64
draining bool
expires time.Time
capabilities []worker.Capability
queue core.ID
capacity uint32
sequence uint64
draining bool
expires time.Time
capabilities []worker.Capability
concurrencyKeys bool
}
func (s *ExecutionStore) welcome(b worker.Binding, state sessionState, now time.Time) worker.Welcome {
@@ -69,9 +70,10 @@ func (s *ExecutionStore) Connect(ctx context.Context, token auth.Token, in worke
return binding, out, err
}
registration, err := requestDigest(struct {
Queue core.ID
Capabilities []worker.Capability
}{in.QueueID, in.Capabilities})
Queue core.ID
Capabilities []worker.Capability
ConcurrencyKeys bool `json:"ConcurrencyKeys,omitempty"`
}{in.QueueID, in.Capabilities, in.ConcurrencyKeys})
if err != nil {
return binding, out, err
}
@@ -116,12 +118,12 @@ func (s *ExecutionStore) Connect(ctx context.Context, token auth.Token, in worke
return err
}
binding = worker.Binding{InstanceID: in.InstanceID, SessionID: session, Epoch: epoch + 1, Proof: proof}
state := sessionState{queue: in.QueueID, capacity: in.Capacity, draining: draining, expires: now.Add(s.options.SessionTTL), capabilities: in.Capabilities}
state := sessionState{queue: in.QueueID, capacity: in.Capacity, draining: draining, expires: now.Add(s.options.SessionTTL), capabilities: in.Capabilities, concurrencyKeys: in.ConcurrencyKeys}
caps, err := json.Marshal(in.Capabilities)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, `INSERT INTO pulse_worker_sessions(namespace_id,id,worker_instance_id,credential_id,node_id,session_epoch,capabilities,capacity,draining,expires_at) VALUES (?,?,?,?,?,?,?,?,?,?)`, token.NamespaceID, session, in.InstanceID, token.CredentialID, s.node, binding.Epoch, string(caps), in.Capacity, draining, state.expires)
_, err = tx.ExecContext(ctx, `INSERT INTO pulse_worker_sessions(namespace_id,id,worker_instance_id,credential_id,node_id,session_epoch,capabilities,capacity,draining,expires_at,concurrency_keys) VALUES (?,?,?,?,?,?,?,?,?,?,?)`, token.NamespaceID, session, in.InstanceID, token.CredentialID, s.node, binding.Epoch, string(caps), in.Capacity, draining, state.expires, in.ConcurrencyKeys)
if err != nil {
return err
}
@@ -172,7 +174,7 @@ func (s *ExecutionStore) lockSession(ctx context.Context, tx *sql.Tx, token auth
var node, instance, sessionCredential core.ID
var sessionEpoch uint64
var raw []byte
err = tx.QueryRowContext(ctx, `SELECT node_id,worker_instance_id,credential_id,session_epoch,capacity,capacity_sequence,capabilities,expires_at FROM pulse_worker_sessions WHERE namespace_id=? AND id=? FOR UPDATE`, token.NamespaceID, b.SessionID).Scan(&node, &instance, &sessionCredential, &sessionEpoch, &state.capacity, &state.sequence, &raw, &state.expires)
err = tx.QueryRowContext(ctx, `SELECT node_id,worker_instance_id,credential_id,session_epoch,capacity,capacity_sequence,capabilities,expires_at,concurrency_keys FROM pulse_worker_sessions WHERE namespace_id=? AND id=? FOR UPDATE`, token.NamespaceID, b.SessionID).Scan(&node, &instance, &sessionCredential, &sessionEpoch, &state.capacity, &state.sequence, &raw, &state.expires, &state.concurrencyKeys)
if errors.Is(err, sql.ErrNoRows) {
return state, worker.StaleSession
}