Nobody has started this yet — be first.
Business impact
Every event this service has ever ingested since this bug was introduced has its device_id and event_type columns swapped in the database. Nothing crashes: both columns are TEXT, both arrays are the same length, Postgres has no way to know the meaning of the values it's being handed doesn't match the column names they're going into. Any downstream consumer of this table — analytics grouping by device_id, an alert rule filtering on event_type = 'error', a support tool looking up "what has device X sent" — silently gets nonsense results that look superficially plausible, which makes this the kind of bug that can go undetected for a very long time and corrupt a correspondingly large amount of historical data before anyone notices something is off. This ticket requires a real Postgres to run at all — the bug is invisible to any test that only checks "did a row get inserted," it only shows up when you check which column a value landed in.
Problem
AsyncpgInserter.insert_batch builds three parallel lists (event_keys, device_ids, event_types) and passes them to conn.execute for an INSERT whose column list is (event_key, device_id, event_type, payload, occurred_at) — but the 2nd and 3rd positional arguments passed to conn.execute are in the wrong order relative to that column list.
Current behavior
conn.execute(INSERT_SQL, event_keys, event_types, device_ids, payloads, occurred_ats) puts event_types where device_id belongs and device_ids where event_type belongs; since both are TEXT[], asyncpg has no type mismatch to catch the swap.
Expected behavior
The order of arguments passed to conn.execute must exactly match INSERT_SQL's column list: event_key, device_id, event_type, payload, occurred_at.
Steps to reproduce
export TEST_DATABASE_URL="postgresql://<you>@localhost/ingest_test" (see project README for setup)Why this matters
Passing several same-typed positional arguments to a parameterized query is a well-known footgun precisely because the type system can't catch a reordering — TEXT[] and TEXT[] are indistinguishable to asyncpg no matter which Python list ends up in which slot. The only thing that catches this class of bug is checking the query's column list against the call site's argument order, by hand, every time — or writing a test that checks which value ends up in which column rather than just "did a row appear."
Suggested approach
Line up INSERT_SQL's column list (app/db.py) against the argument order in the conn.execute(...) call in AsyncpgInserter.insert_batch, position by position.
Acceptance criteria
Verification
TEST_DATABASE_URL=postgresql://<you>@localhost/ingest_test pytest practice_tickets/tests/test_ticket_17_column_swap.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/device-id-event-type-swapFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/device-id-event-type-swapSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:8000localhost:5432curl localhost:8000/healthzcurl localhost:8000/readyzWork the tickets in practice_tickets/tickets/ (ordered easy → hard); each names one dedicated test in practice_tickets/tests/:
pytest practice_tickets/tests/test_ticket_01_persisted_count.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 20, clean pass/fail summary
TICKET-17 needs a real Postgres to run at all (it checks which column a value lands in, which no fake/in-memory DB backend can meaningfully verify) — set TEST_DATABASE_URL=postgresql://<you>@localhost/ingest_test before running it or the full suite; without it, that one test is skipped, not failed.
Level 1
Fix a bug
Read existing behaviour, correct it.