SIGN IN SIGN UP

engine: crash safe local state, the journal, and the branch lock

Adds the three packages the "nothing outlives its environment" guarantee rests
on.

internal/state is SQLite in write ahead logging mode under .antifailure, opened
with a pure Go driver so the shipped binary needs no C toolchain and keeps
CGO_ENABLED=0. It holds references, never secrets and never customer data. A
database that fails its integrity check is moved aside and rebuilt rather than
refused, because refusing to start would leave a user holding resources they
can no longer tear down, which is the worst outcome for a tool whose whole
promise is teardown.

internal/journal records every external resource as an intent before it is
created and compensates it on teardown. Two choices make replay survive
reality: the compensating action is stored as data, never as code, so a binary
built six months later can tear down what this one made; and every create uses
a deterministic idempotency key, so a provider call that times out after the
resource was created finds the existing intent instead of orphaning it.

Replay never stops at the first failure. A provider being unreachable must not
strand the other twelve resources, so each record is attempted, failures are
recorded with an attempt count for the next teardown, and the result says what
is left. Records for a provider this binary has no deleter for are skipped
rather than dropped, so a downgrade cannot orphan anything.

internal/lock is the per branch advisory lock. The interesting half is telling
an active process from a corpse holding the door: the lock file carries the
owner's process identifier, host, and start time, and a stale lock is reclaimed
by liveness check rather than by timeout. A lock written on another host is
treated as live, because wrongly waiting costs a wait and wrongly proceeding
costs two processes fighting over one environment.

One real defect, found by a test rather than by reading: state.Close set the
handle to nil, so every journal call after it panicked. Those calls are exactly
the ones that race Close during teardown, so the panic would have crashed the
process while it was deleting resources. Close now keeps the handle and the
driver returns an error instead.

Failure paths covered:
  crash at any point in the create sequence: TestReplay_ConvergesFromACrashAtEveryStep
  any interleaving of intent, create, commit, crash: TestReplay_AnyInterleavingConvergesToZeroLiveResources (property)
  a retry after a timeout duplicates a resource: TestIntent_SameKeyReturnsTheExistingRecord
  concurrent intents for one key: TestIntent_ConcurrentSameKeyYieldsOneRecord
  a provider is unreachable during teardown: TestReplay_ContinuesPastAFailureAndReportsWhatIsLeft
  a downgrade orphans resources: TestReplay_LeavesRecordsWithNoDeleterAlone
  teardown is cancelled midway: TestReplay_StopsOnACancelledContextAndLeavesTheRest
  the state database is closed mid teardown: TestJournal_EveryMethodReportsAClosedDatabase
  the state database is corrupt: TestOpen_RebuildsACorruptDatabaseAndKeepsTheOriginal
  a stale lock blocks forever: TestAcquire_ReclaimsALockHeldByADeadProcess
  a stale holder deletes a newer lock: TestRelease_DoesNotRemoveALockANewerProcessHolds

Coverage: journal 94.9 percent, state 83.3 percent, lock 77.5 percent.
Signed-off-by: Vir Sanghavi <Virrsanghavi@gmail.com>
V
Vir Sanghavi committed
86a59e3d1a630b5a077bfb600981d0b79e0cc32b
Parent: 7c292fc