Skip to content

Config sync can overwrite newer local changes with a stale snapshot result #304

Description

@michaelw

Summary

The sync path can overwrite a newer local config change with a stale remote-derived result because it reads a snapshot, normalizes off-lock, and then unconditionally writes that result back later.

This is distinct from the basic "sync result not applied" bug. Even after the apply path is corrected, the algorithm still allows stale-snapshot lost updates.

Minimal repro

I checked out the sync-apply fix only (without any generation/retry protection), added a focused repro test, and ran:

go test ./internal/config -run 'TestSyncStaleSnapshotOverwritesConcurrentRoleUpdate'

Repro test intent:

  1. capture the same role snapshot the pre-retry sync path uses
  2. derive the normalized remote result from that snapshot
  3. apply a newer local write
  4. commit the stale normalized result anyway
  5. observe the newer local write being overwritten

Representative failing output:

--- FAIL: TestSyncStaleSnapshotOverwritesConcurrentRoleUpdate (0.00s)
    sync_concurrency_issue_test.go:56:
        expected: "local-newer"
        actual  : "remote-update"
        Messages:    stale sync result should not overwrite a newer local update

Analysis

The problem is not just "concurrent work exists"; the current algorithm has a specific stale-snapshot window:

  • snapshot current config
  • merge/normalize off-lock
  • write the result later with no compare-and-swap or retry

If local config changes in that window, the older derived result can win.

Scope

This issue is only about stale-snapshot lost updates in the sync path.

Please keep separate from:

  • the base bug where sync results were not applied to live config at all
  • broader lock-discipline cleanup across config readers/writers

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions