Gitlab Datastore
Stores swamp runtime data in GitLab using the Terraform state HTTP API. Provides distributed locking via GitLab's native state locking mechanism and bidirectional sync between local cache and GitLab. Emits OpenTelemetry spans for GitLab API calls, lock acquisition/release, and push/pull sync so datastore activity is visible in traces when a TracerProvider is configured.
2026.07.30.1
Fixed: Lock contention on back-to-back model operations. Previously all
lock paths mapped to a single GitLab Terraform state ({prefix}--lock),
causing the second operation's sync push to time out waiting for the first to
release. Locks are now per-path — each datastore path gets its own lock state
({prefix}--lock--{sanitized-path}), eliminating cross-model contention
entirely.
Changed: Default lock timing constants tuned for actual push latency.
TTL reduced from 30s to 10s, retry interval from 1s to 500ms, max wait from
60s to 30s. Stale lock detection remains at 60s (threshold is now configurable
via staleLockThresholdMs and decoupled from TTL).
Upgrade note: Existing locks held under the old single-state name
({prefix}--lock) will not conflict with the new per-path names. No migration
required — old lock states become orphaned and can be cleaned up via the GitLab
Terraform states UI if desired.
Stores swamp runtime data in GitLab using the Terraform state HTTP API with native locking support
Config Fields
| Field | Type | Description |
|---|---|---|
| projectId | string | GitLab project ID (numeric) or URL-encoded path (e.g., 'mygroup/myproject') |
| baseUrl | string | GitLab instance URL |
| token | string | GitLab personal access token or CI job token with api scope |
| username? | string | GitLab username (optional, defaults to 'gitlab-ci-token') |
| statePrefix | string | Prefix for state names to namespace swamp data |
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. All GitLab API
calls now flow through a single choke point that emits one span each
(GitLab getState, GitLab putState, GitLab lock, GitLab listStates, …)
carrying the HTTP method, response status, project ID, state name, and server
host. The lock emits gitlab-datastore lock acquire / release / withLock /
inspect / forceRelease, with acquire recording wait duration, contention,
and the current holder. The sync service emits
gitlab-datastore pullChanged / pushChanged / hydrateFile / preparePush /
commitPush with file counts and the number of states listed.
Added: Lock retries are recorded as retry span events, distinguishing
ordinary contention from stealing a stale lock.
Changed: Non-2xx responses now mark their span as an error, except where a
status is normal control flow — a 404 from getState meaning the state does
not exist, a 409 or 423 from lock meaning another holder has it, a 404 or 204
from getLockInfo meaning the state is unlocked. This client inspects
response.status by hand rather than throwing, so without the distinction a
span would have reported success on a 500.
Changed: pushChanged records datastore.dirty_path_mode rather than
datastore.fast_path_hit. In the sibling extensions fast_path_hit means no
work was done; this method has no short-circuit, and a dirty-path push that
uploads files is not a fast path.
Changed: Nothing observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered. Existing
behaviour, return values, and log output are unchanged.
Note on secrets: Every request carries a PRIVATE-TOKEN header and request bodies carry file content. Neither headers nor bodies are ever recorded — span attributes hold only the operation name, HTTP method, status, project ID, state name, and host. A test asserts no span attribute contains the token.
updated labels
2026.07.18.1
Changed: Version bump only, no code changes.
2026.07.16.1
Changed: Internal-only version bump. PR #183 added a fmt:check task to deno.json, which is not part of the published bundle — this release's published content is identical to 2026.06.30.1.
2026.07.16.1
Changed: Internal-only version bump. PR #183 added a fmt:check task to deno.json, which is not part of the published bundle — this release's published content is identical to 2026.06.30.1.
2026.06.30.1
Two-Phase Datastore Sync
Adds preparePush/commitPush two-phase sync protocol alongside the existing
pushChanged single-shot path.
Added
TwoPhaseSyncServiceinterface withpreparePush()andcommitPush()methodsPushManifestopaque branded type for safe manifest passing between phasespreparePush()collects diff outside the lock (no remote writes)commitPush()re-reads fresh state under lock, uploads entries, merges hashescapabilities()now reportstwoPhaseSync: true
Changed
- Extracted
EXCLUDED_DIRSandisExcludedFileto module-level constants (deduplication) - No breaking changes to existing
pushChangedbehavior
updated platforms
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned