Skip to content

fix: normalize nil event metadata to an empty map#973

Merged
alexluong merged 3 commits into
mainfrom
fix/event-nil-metadata
Jun 24, 2026
Merged

fix: normalize nil event metadata to an empty map#973
alexluong merged 3 commits into
mainfrom
fix/event-nil-metadata

Conversation

@alexluong

Copy link
Copy Markdown
Collaborator

Closes #972.

Problem

An event published without metadata carries a nil map[string]string. events.metadata and attempts.event_metadata are jsonb NOT NULL, and under pgx v5.10 (bumped in #940) a nil map encodes to SQL NULL — so the Postgres log store rejects the insert:

null value in column "metadata" of relation "events_default" violates not-null constraint

The event is never persisted; the log worker retries forever. ClickHouse accepted the insert but read metadata back as nil — a silent inconsistency with Postgres.

Why it wasn't caught

  • The logstore conformance suite only ever inserted events from EventFactory.Any(), which always sets a populated metadata map — the nil path was never exercised.
  • Those conformance tests are integration-gated (testing.Short()), and CI's Go job runs -short, so they don't run on PRs. Only the spec-sdk-tests e2e job hit it, surfacing late as a confusing timeout.
  • An apirouter test couldn't have caught it either: those tests use the in-memory store, and publish is async (the insert happens downstream of the HTTP response).

Change

Treat nil and empty metadata as equivalent ("no metadata") and enforce a non-nil map at every boundary:

  • apirouter — default missing metadata to {} when building the event (the canonical, earliest boundary; runs in CI).
  • pglogstore — default nil to {} in the event and attempt insert arrays (correctness; NOT NULL).
  • chlogstore — default nil to {} before marshaling, so it round-trips as an empty map like Postgres (consistency).

Commits

  1. test: — failing coverage at both layers. Red on current code: Postgres rejects the insert, ClickHouse reads back nil, the router passes nil through.
  2. fix: — the normalization above. All green.

Verification

Local, both backends (make up/test, TESTINFRA=1):

  • apirouter publish tests — pass (runs in CI -short).
  • pglogstore + chlogstore conformance — pass.
  • gofmt / go vet clean.

Note

The conformance tests guard the real backends but only run in the non--short integration runs, not on PRs. The new apirouter test does run in CI. Wiring the logstore integration tests into CI is a worthwhile follow-up but out of scope here.

🤖 Generated with Claude Code

alexluong and others added 3 commits June 24, 2026 01:54
`events.metadata` (and `attempts.event_metadata`) are `jsonb NOT NULL`,
but an event published without metadata carries a nil `map[string]string`.
Nothing exercised that path, so it regressed silently when a pgx bump
changed nil-map encoding to SQL NULL.

Add coverage at both layers:

- apirouter: publishing without metadata must build an event with a
  non-nil (empty) metadata map.
- logstore conformance (pg + ch): inserting an event with nil metadata
  must not error and must round-trip as a non-nil empty map.

All three are red on current code: Postgres rejects the insert
(NOT NULL); ClickHouse inserts but reads the metadata back as nil; the
router passes the nil map straight through.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An event published without metadata carries a nil map. `events.metadata`
and `attempts.event_metadata` are `jsonb NOT NULL`, and under pgx v5.10 a
nil map encodes to SQL NULL, so the Postgres log store rejects the insert
and the event is never persisted. ClickHouse accepted it but read the
metadata back as nil, an inconsistency with Postgres.

Treat nil and empty metadata as equivalent ("no metadata") and enforce a
non-nil map at every boundary:

- apirouter: default missing metadata to {} when building the event.
- pglogstore: default nil to {} in the event and attempt insert arrays.
- chlogstore: default nil to {} before marshaling, so it round-trips as
  an empty map like Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The shared logstore conformance suite also runs against memlogstore
(in-memory, exercised in the -short CI unit job). cloneEvent already
defaults a nil MatchedDestinationIDs to an empty slice but left Metadata
nil, so the new "nil metadata round-trips as empty" contract failed there.
Mirror the existing convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexluong alexluong merged commit 10985f0 into main Jun 24, 2026
3 checks passed
@alexluong alexluong deleted the fix/event-nil-metadata branch June 24, 2026 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(pglogstore): events with no metadata fail to insert (jsonb NOT NULL) under pgx v5.10

2 participants