Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
97a42ba
tso: add dedicated ceiling fsm
bootjp Jul 19, 2026
9182f37
tso: route durable timestamps through group leader
bootjp Jul 19, 2026
aa94f4e
tso: route durable timestamps through group leader (#1108)
bootjp Jul 19, 2026
07460ec
tso: complete centralized runtime semantics
bootjp Jul 19, 2026
416c056
tso: complete centralized runtime semantics
bootjp Jul 19, 2026
9b9b7cf
tso: add runtime operations and observability
bootjp Jul 19, 2026
229c206
proto: reserve removed raft status fields
bootjp Jul 23, 2026
917f421
tso: preserve runtime allocator handles
bootjp Jul 23, 2026
2402e77
tso: cover runtime allocator reload
bootjp Jul 23, 2026
360418a
tso: bind applied read vouchers
bootjp Jul 23, 2026
f641453
proto: reserve retired raft status fields
bootjp Jul 23, 2026
c8e3ada
adapter: align timestamp validation status test
bootjp Jul 23, 2026
580c5b5
tso: preserve applied-read vouchers through gated dispatch
bootjp Jul 23, 2026
874a14f
tso: preserve applied-read vouchers across adapters
bootjp Jul 23, 2026
5587fd5
adapter: preserve Phase D read vouchers
bootjp Jul 23, 2026
ca1a812
Implement centralized TSO phase D (#1114)
bootjp Jul 23, 2026
227aff5
Merge main into dedicated TSO design
bootjp Jul 24, 2026
330425a
Merge dedicated TSO base into runtime operations
bootjp Aug 7, 2026
c231fdb
Merge main into dedicated TSO design
bootjp Aug 7, 2026
52519ea
Merge dedicated TSO design into runtime operations
bootjp Aug 7, 2026
96c7573
Complete centralized TSO runtime operations (#1129)
bootjp Aug 7, 2026
9427a0a
adapter: propagate CreateTable dispatch failures
bootjp Aug 21, 2026
1bdb02b
docs: record the open Phase-D wall-clock limitation
bootjp Aug 21, 2026
7b58ac8
tso: publish durable marker state from the FSM
bootjp Aug 23, 2026
40cca66
fs: bind a Phase-D read voucher to filesystem transactions
bootjp Aug 23, 2026
0132980
fs,tso: close two Phase-D holes on the dedicated TSO group
bootjp Aug 23, 2026
4929104
main: extract internalOptionsForGroup
bootjp Aug 23, 2026
0b37679
tso: reject minima beyond the committed ceiling before observing
bootjp Aug 24, 2026
ea2014c
tso: keep legacy warm-up off the pinned timestamp group
bootjp Aug 24, 2026
a010d06
Merge main into dedicated TSO design
bootjp Aug 25, 2026
7ece810
tso: validate phase-d timestamp routing
bootjp Aug 25, 2026
a74ad8f
tso: close phase-d review gaps
bootjp Aug 26, 2026
5d5c40f
tso: tighten review edge cases
bootjp Aug 26, 2026
6b06c05
tso: pin the timestamp group when Phase D activates later
bootjp Aug 27, 2026
31392f5
filesystem: normalize the empty-store read timestamp
bootjp Aug 27, 2026
88bcf81
tso: validate pre-stamped forwarded timestamps in Phase D
bootjp Aug 27, 2026
0cdd704
tso: keep the Phase-D floor monotonic across restore
bootjp Aug 27, 2026
7dbf740
tso: keep pre-Phase-D transaction resolution working
bootjp Aug 27, 2026
3a81cb9
tso: narrow the legacy carve-out and validate forwarded start timestamps
bootjp Aug 28, 2026
a56034f
tso: close two ways around the forwarded-timestamp checks
bootjp Aug 28, 2026
4870f95
tso: allocate rollback timestamps instead of deriving them
bootjp Aug 28, 2026
d73292b
tso: close the remaining forwarded-timestamp bypasses
bootjp Aug 28, 2026
4fe5b62
tso: do not persist a derived rollback timestamp
bootjp Aug 28, 2026
a062200
tso: authorize marker activation against local rollout configuration
bootjp Aug 28, 2026
5d3dce2
tso: stop the activation test from racing on the mode flags
bootjp Aug 28, 2026
b91ae6e
tso: let an already-durable marker through the activation gate
bootjp Aug 28, 2026
1132828
tso: expose cutover state on the production allocator
bootjp Aug 28, 2026
721ca14
Merge origin/main into design/dedicated-tso-fsm
bootjp Aug 28, 2026
c139b03
tso: emit the shortest snapshot layout that fits the state
bootjp Aug 29, 2026
357399c
Merge origin/main into design/dedicated-tso-fsm
bootjp Aug 29, 2026
aabf40a
main: wire the read tracker into the shard group builder
bootjp Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
321 changes: 296 additions & 25 deletions adapter/distribution_server.go

Large diffs are not rendered by default.

557 changes: 557 additions & 0 deletions adapter/distribution_server_test.go

Large diffs are not rendered by default.

34 changes: 24 additions & 10 deletions adapter/dynamodb_item_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ func (d *DynamoDBServer) retryItemWriteWithGenerationLegacy(
backoff := transactRetryInitialBackoff
deadline := time.Now().Add(transactRetryMaxDuration)
for range transactRetryMaxAttempts {
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb item-write legacy: begin read timestamp")
if err != nil {
return nil, errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
plan, err := prepare(readTS)
if err != nil {
return nil, err
Expand All @@ -164,7 +168,7 @@ func (d *DynamoDBServer) retryItemWriteWithGenerationLegacy(
return plan, nil
}
plan.req.StartTS = readTS
if err = d.commitItemWrite(ctx, plan.req); err != nil {
if err = d.commitItemWrite(ctx, readTimestamp, plan.req); err != nil {
if !isRetryableTransactWriteError(err) {
return nil, errors.WithStack(err)
}
Expand Down Expand Up @@ -204,6 +208,10 @@ type reusableItemWrite struct {
// — the write set was built once from attempt 1's read — so plan is also the
// correct value to return when the FSM dedup no-ops the apply (R1).
plan *itemWritePlan
// readTimestamp carries the Phase-D dispatch capability that validates the
// reused StartTS. Retaining only the numeric StartTS would leave retries
// unable to re-vouch the same applied read watermark.
readTimestamp kv.ReadTimestamp
// commitTS is the most recent dispatched commit_ts for this write set; the
// next retry passes it as PrevCommitTS so the FSM probes exactly the attempt
// that might have landed.
Expand Down Expand Up @@ -266,7 +274,11 @@ func (d *DynamoDBServer) itemWriteFirstAttempt(
tableName string,
prepare func(readTS uint64) (*itemWritePlan, error),
) (*itemWritePlan, *reusableItemWrite, error) {
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb item-write: begin read timestamp")
if err != nil {
return nil, nil, errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
plan, err := prepare(readTS)
if err != nil {
return nil, nil, err
Expand All @@ -283,13 +295,14 @@ func (d *DynamoDBServer) itemWriteFirstAttempt(
}
plan.req.StartTS = readTS
plan.req.CommitTS = commitTS
if dispErr := d.commitItemWrite(ctx, plan.req); dispErr != nil {
if dispErr := d.commitItemWrite(ctx, readTimestamp, plan.req); dispErr != nil {
// dispErr is already wrapped by commitItemWrite; return it raw.
if isRetryableTransactWriteError(dispErr) {
return nil, &reusableItemWrite{
plan: plan,
commitTS: commitTS,
probeKey: kv.PrimaryKeyForElems(plan.req.Elems),
plan: plan,
readTimestamp: readTimestamp,
commitTS: commitTS,
probeKey: kv.PrimaryKeyForElems(plan.req.Elems),
}, dispErr
}
return nil, nil, dispErr
Expand All @@ -311,7 +324,7 @@ func (d *DynamoDBServer) itemWriteReuseAttempt(
}
pending.plan.req.CommitTS = commitTS
pending.plan.req.PrevCommitTS = pending.commitTS
dispErr := d.commitItemWrite(ctx, pending.plan.req)
dispErr := d.commitItemWrite(ctx, pending.readTimestamp, pending.plan.req)
if dispErr == nil {
return d.finishItemWriteAttempt(ctx, tableName, pending.plan)
}
Expand Down Expand Up @@ -433,8 +446,9 @@ func (d *DynamoDBServer) preparePutItemWrite(ctx context.Context, in putItemInpu
}, nil
}

func (d *DynamoDBServer) commitItemWrite(ctx context.Context, req *kv.OperationGroup[kv.OP]) error {
_, err := d.coordinator.Dispatch(ctx, req)
func (d *DynamoDBServer) commitItemWrite(ctx context.Context, readTimestamp kv.ReadTimestamp, req *kv.OperationGroup[kv.OP]) error {
dispatchCtx := readTimestamp.WithDispatchVoucher(ctx)
_, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, req)
if err != nil {
return errors.WithStack(err)
}
Expand Down
5 changes: 5 additions & 0 deletions adapter/dynamodb_locks.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ func (d *DynamoDBServer) nextTxnReadTS() uint64 {
return maxTS
}

func (d *DynamoDBServer) beginTxnReadTimestamp(ctx context.Context, label string) (kv.ReadTimestamp, error) {
readTimestamp, err := kv.BeginReadTimestampThrough(ctx, d.coordinator, d.nextTxnReadTS(), label)
return readTimestamp, errors.WithStack(err)
}

func (d *DynamoDBServer) pinReadTS(ts uint64) *kv.ActiveTimestampToken {
if d == nil || d.readTracker == nil {
return &kv.ActiveTimestampToken{}
Expand Down
46 changes: 36 additions & 10 deletions adapter/dynamodb_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ func (d *DynamoDBServer) ensureLegacyTableMigrationLocked(ctx context.Context, t
backoff := transactRetryInitialBackoff
deadline := time.Now().Add(transactRetryMaxDuration)
for range transactRetryMaxAttempts {
readTS := d.nextTxnReadTS()
schema, exists, err := d.loadTableSchemaAt(ctx, tableName, readTS)
readTimestamp, schema, migrationRequired, err := d.legacyMigrationSnapshot(ctx, tableName)
if err != nil {
return errors.WithStack(err)
}
if !exists || !schema.needsLegacyKeyMigration() {
if !migrationRequired {
return nil
}
// Admin read-only callers (AdminScanTable) must not trigger
Expand All @@ -39,7 +38,7 @@ func (d *DynamoDBServer) ensureLegacyTableMigrationLocked(ctx context.Context, t
"table requires a one-time legacy-key migration before admin read endpoints are available; migrate via the SigV4 surface first")
}
if !schema.usesOrderedKeyEncoding() {
err = d.startLegacyTableKeyMigration(ctx, schema, readTS)
err = d.startLegacyTableKeyMigration(ctx, schema, readTimestamp)
} else {
err = d.migrateLegacyTableGeneration(ctx, schema)
}
Expand All @@ -57,14 +56,30 @@ func (d *DynamoDBServer) ensureLegacyTableMigrationLocked(ctx context.Context, t
return newDynamoAPIError(http.StatusInternalServerError, dynamoErrInternal, "legacy table migration retry attempts exhausted")
}

func (d *DynamoDBServer) legacyMigrationSnapshot(
ctx context.Context,
tableName string,
) (kv.ReadTimestamp, *dynamoTableSchema, bool, error) {
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb legacy-table migration: begin read timestamp")
if err != nil {
return kv.ReadTimestamp{}, nil, false, errors.WithStack(err)
}
schema, exists, err := d.loadTableSchemaAt(ctx, tableName, readTimestamp.Timestamp())
if err != nil {
return kv.ReadTimestamp{}, nil, false, errors.WithStack(err)
}
return readTimestamp, schema, exists && schema.needsLegacyKeyMigration(), nil
}

func (d *DynamoDBServer) startLegacyTableKeyMigration(
ctx context.Context,
schema *dynamoTableSchema,
readTS uint64,
readTimestamp kv.ReadTimestamp,
) error {
if schema == nil || schema.usesOrderedKeyEncoding() {
return nil
}
readTS := readTimestamp.Timestamp()
nextGeneration, err := d.nextTableGenerationAt(ctx, schema.TableName, readTS)
if err != nil {
return err
Expand All @@ -81,7 +96,8 @@ func (d *DynamoDBServer) startLegacyTableKeyMigration(
return err
}
req.StartTS = readTS
if _, err := d.coordinator.Dispatch(ctx, req); err != nil {
dispatchCtx := readTimestamp.WithDispatchVoucher(ctx)
if _, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, req); err != nil {
return errors.WithStack(err)
}
return nil
Expand Down Expand Up @@ -151,7 +167,11 @@ func (d *DynamoDBServer) migrateLegacyItem(
backoff := transactRetryInitialBackoff
deadline := time.Now().Add(transactRetryMaxDuration)
for range transactRetryMaxAttempts {
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb legacy-item migration: begin read timestamp")
if err != nil {
return errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
req, done, err := d.buildLegacyMigrationRequest(ctx, targetSchema, sourceSchema, targetKey, sourceKey, readTS)
if err != nil {
return err
Expand All @@ -160,7 +180,8 @@ func (d *DynamoDBServer) migrateLegacyItem(
return nil
}
req.StartTS = readTS
if _, err := d.coordinator.Dispatch(ctx, req); err == nil {
dispatchCtx := readTimestamp.WithDispatchVoucher(ctx)
if _, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, req); err == nil {
return nil
} else if !isRetryableTransactWriteError(err) {
return errors.WithStack(err)
Expand Down Expand Up @@ -292,15 +313,20 @@ func (d *DynamoDBServer) finalizeLegacyTableMigration(ctx context.Context, schem
if err != nil {
return errors.WithStack(err)
}
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb finalize migration: begin read timestamp")
if err != nil {
return errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
req := &kv.OperationGroup[kv.OP]{
IsTxn: true,
StartTS: readTS,
Elems: []*kv.Elem[kv.OP]{
{Op: kv.Put, Key: dynamoTableMetaKey(schema.TableName), Value: body},
},
}
if _, err := d.coordinator.Dispatch(ctx, req); err != nil {
dispatchCtx := readTimestamp.WithDispatchVoucher(ctx)
if _, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, req); err != nil {
return errors.WithStack(err)
}
d.launchDeletedTableCleanup(schema.TableName, oldGeneration)
Expand Down
17 changes: 17 additions & 0 deletions adapter/dynamodb_onephase_dedup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,23 @@ func TestItemWriteDedup_PriorAttemptDidNotLand_Applies(t *testing.T) {
require.Equal(t, 0, coord.probeNoOps, "nothing landed, so the probe must miss and the reuse applies")
}

func TestItemWriteDedup_PhaseDVouchesReuse(t *testing.T) {
t.Parallel()
ctx := context.Background()
st := store.NewMVCCStore()
coord := newPhaseDDedupTestCoordinator(st, 1, false)
schema, server := newDedupItemWriteServer(st, coord, true)
seedDedupItem(t, st, schema, "1", "2")

plan, err := server.updateItemWithRetry(ctx, appendListInput())
require.NoError(t, err)
require.NotNil(t, plan)

require.Equal(t, []string{"1", "2", "3"}, readListValues(t, server, schema))
require.Equal(t, 2, coord.dispatches)
require.Equal(t, uint64(2), coord.vouches.Load(), "first attempt and reused write set must each reserve a Phase-D dispatch voucher")
}

// TestItemWriteDedup_SelfInflictedReuseConflict_ReturnsSuccess: attempt 1
// pre-rejects, the reuse then LANDS but surfaces WriteConflict (self-inflicted
// conflict under churn). The adapter-side self-conflict guard probes the reuse's
Expand Down
24 changes: 17 additions & 7 deletions adapter/dynamodb_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ func (d *DynamoDBServer) createTableWithRetry(ctx context.Context, tableName str
backoff := transactRetryInitialBackoff
deadline := time.Now().Add(transactRetryMaxDuration)
for range transactRetryMaxAttempts {
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb create table: begin read timestamp")
if err != nil {
return errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
exists, err := d.tableExistsAt(ctx, tableName, readTS)
if err != nil {
return err
Expand All @@ -199,10 +203,11 @@ func (d *DynamoDBServer) createTableWithRetry(ctx context.Context, tableName str
if err != nil {
return err
}
if _, err := d.coordinator.Dispatch(ctx, req); err == nil {
req.StartTS = readTS
dispatchCtx := readTimestamp.WithDispatchVoucher(ctx)
if _, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, req); err == nil {
return nil
}
if !isRetryableTransactWriteError(err) {
} else if !isRetryableTransactWriteError(err) {
return errors.WithStack(err)
}
if err := waitRetryWithDeadline(ctx, deadline, backoff); err != nil {
Expand Down Expand Up @@ -293,7 +298,11 @@ func (d *DynamoDBServer) deleteTableWithRetry(ctx context.Context, tableName str
backoff := transactRetryInitialBackoff
deadline := time.Now().Add(transactRetryMaxDuration)
for range transactRetryMaxAttempts {
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb delete table: begin read timestamp")
if err != nil {
return errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
schema, exists, err := d.loadTableSchemaAt(ctx, tableName, readTS)
if err != nil {
return errors.WithStack(err)
Expand All @@ -304,12 +313,13 @@ func (d *DynamoDBServer) deleteTableWithRetry(ctx context.Context, tableName str

req := &kv.OperationGroup[kv.OP]{
IsTxn: true,
StartTS: 0,
StartTS: readTS,
Elems: []*kv.Elem[kv.OP]{
{Op: kv.Del, Key: dynamoTableMetaKey(tableName)},
},
}
if _, err := d.coordinator.Dispatch(ctx, req); err != nil {
dispatchCtx := readTimestamp.WithDispatchVoucher(ctx)
if _, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, req); err != nil {
if !isRetryableTransactWriteError(err) {
return errors.WithStack(err)
}
Expand Down
25 changes: 19 additions & 6 deletions adapter/dynamodb_transact.go
Original file line number Diff line number Diff line change
Expand Up @@ -695,14 +695,15 @@ func (d *DynamoDBServer) transactWriteItemsWithRetry(ctx context.Context, in tra

func (d *DynamoDBServer) runTransactWriteAttempt(
ctx context.Context,
reqs *kv.OperationGroup[kv.OP],
reqs *preparedTransactWriteItemsRequest,
generations map[string]uint64,
cleanupKeys [][]byte,
) (bool, error, error) {
if len(reqs.Elems) == 0 {
if len(reqs.group.Elems) == 0 {
return true, nil, nil
}
if _, err := d.coordinator.Dispatch(ctx, reqs); err != nil {
dispatchCtx := reqs.readTimestamp.WithDispatchVoucher(ctx)
if _, err := kv.DispatchWithReadTimestamp(dispatchCtx, d.coordinator, reqs.group); err != nil {
wrapped := errors.WithStack(err)
if !isRetryableTransactWriteError(err) {
return false, nil, wrapped
Expand All @@ -723,7 +724,12 @@ func (d *DynamoDBServer) runTransactWriteAttempt(
return false, nil, nil
}

func (d *DynamoDBServer) buildTransactWriteItemsRequest(ctx context.Context, in transactWriteItemsInput) (*kv.OperationGroup[kv.OP], map[string]uint64, [][]byte, error) {
type preparedTransactWriteItemsRequest struct {
readTimestamp kv.ReadTimestamp
group *kv.OperationGroup[kv.OP]
}

func (d *DynamoDBServer) buildTransactWriteItemsRequest(ctx context.Context, in transactWriteItemsInput) (*preparedTransactWriteItemsRequest, map[string]uint64, [][]byte, error) {
tableNames, err := collectTransactWriteTableNames(in)
if err != nil {
return nil, nil, nil, err
Expand All @@ -733,7 +739,11 @@ func (d *DynamoDBServer) buildTransactWriteItemsRequest(ctx context.Context, in
return nil, nil, nil, err
}
}
readTS := d.nextTxnReadTS()
readTimestamp, err := d.beginTxnReadTimestamp(ctx, "dynamodb transact-write: begin read timestamp")
if err != nil {
return nil, nil, nil, errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()
reqs := &kv.OperationGroup[kv.OP]{
IsTxn: true,
// Keep transaction start aligned with the snapshot used to evaluate
Expand All @@ -752,7 +762,10 @@ func (d *DynamoDBServer) buildTransactWriteItemsRequest(ctx context.Context, in
return nil, nil, nil, err
}
}
return reqs, tableGenerations, cleanup, nil
return &preparedTransactWriteItemsRequest{
readTimestamp: readTimestamp,
group: reqs,
}, tableGenerations, cleanup, nil
}

// processTransactWriteItem validates and plans a single item within a
Expand Down
Loading
Loading