BoltDB Schema Management#
Overview#
Atlantis uses bbolt (an embedded key-value store) as its persistent data layer, stored as atlantis.db in the configured data directory . The BoltDB struct wraps the raw bolt database and provides three named buckets:
| Bucket | Constant | Purpose |
|---|---|---|
runLocks | locksBucketName | Per-project/workspace plan locks |
pulls | pullsBucketName | Pull request plan/apply status |
globalLocks | globalLocksBucketName | Command-level locks (e.g., apply lock) |
All values are JSON-serialized Go structs. There is no formal schema versioning β backward compatibility is managed through startup migrations, tolerate-and-overwrite logic, and preserve-on-reset semantics.
Lock Key Schema (runLocks bucket)#
Key Format#
Lock keys were migrated from a 3-part to a 4-part format to support named projects:
- Old format:
{repoFullName}/{path}/{workspace} - New format:
{repoFullName}/{path}/{workspace}/{projectName}
The canonical key producer is models.GenerateLockKey. Validity is checked by locking.IsCurrentLocking, which applies a regex requiring exactly four path segments.
Startup Migration#
At startup, New() runs a one-time migration inside a BoltDB write transaction:
- Scans every key in
runLocks. - For keys that fail
locking.IsCurrentLocking(i.e., old 3-part format), deserializes the value and generates a new key viamodels.GenerateLockKey. - Writes the value under the new key and deletes the old key.
Failures are logged as warnings but do not abort startup . Old-format keys continue to work until the migration succeeds.
Pull Status Schema (pulls bucket)#
Key Format#
Pull status keys follow the pattern {hostname}::{repoFullName}::{pullNum}, validated to reject any component containing the :: separator .
Value Structure#
Each key stores a JSON-serialized models.PullStatus, containing:
Pullβ amodels.PullRequestsnapshot withHeadCommitandBaseBranchas freshness fields.Projectsβ a[]models.ProjectStatusslice, each holdingStatusandPolicyStatus.
Write Logic and Freshness#
UpdatePullWithResults is the single write path. On every plan or apply result it:
- Reads the current entry.
- Calls
pullStatusOutdatedForPull: returnstrueifHeadCommitdiffers, or ifBaseBranchchanged (same-head retargets require a new plan). - If outdated (reset): starts a fresh
PullStatusfrom the new results but copies forward anyPolicyStatusfrom matching old projects . This preserves policy approvals in the window between the plan DB write and the subsequent policy-check DB write. - If current (merge): updates only the projects present in
newResults, leaving unrelated projects untouched .
Backward Compatibility: Tolerate-and-Overwrite#
If getPullFromBucket returns a deserialization error (e.g., after a schema change renders existing JSON unreadable), UpdatePullWithResults logs a warning and treats the entry as absent, overwriting it with fresh data . In-flight policy approvals in the old blob are lost, but the server does not crash or block.
Policy Approval Schema (PolicySetStatus)#
Evolution (PR #6271)#
PR #6271 "feat: sticky policy approvals" replaced a simple integer approval count with a structured, hash-based model:
| Field (old) | Field (new) | Notes |
|---|---|---|
CurApprovals int | Approvals []PolicySetApproval | Tracks per-approver approval with hash snapshot |
ReqApprovals int | ReqApprovalCount int | Renamed |
| (absent) | Hashes []string | Current policy output hashes |
| (absent) | PolicyItemRegex string | Regex for extracting hashed items |
The PolicySetApproval struct stores Approver string and Hashes []string (the policy item hashes at time of approval). PolicySetStatus.GetCurApprovals() counts only approvals whose recorded hashes still cover all current Hashes, filtering out stale approvals.
Approval Persistence Across Commits#
When a commit is pushed (reset path in UpdatePullWithResults), existing PolicyStatus is copied forward to the new PullStatus entry . The doPolicyCheck runner then applies sticky-approval carry-forward logic and overwrites these with validated results.
Backward Compatibility#
The same tolerate-and-overwrite mechanism described above handles old blobs serialized with integer Approvals fields. The schema breakage is logged; the PR was written with explicit acceptance that in-flight approvals are lost on first upgrade .
Key Source Files#
| File | Relevance |
|---|---|
server/core/boltdb/boltdb.go | All DB operations, startup migration, bucket definitions |
server/events/models/models.go | PullStatus, ProjectStatus, PolicySetStatus, PolicySetApproval, GenerateLockKey |
server/core/locking/locking.go | IsCurrentLocking key regex validation |
server/events/db_updater.go | Filters stale results before DB write |