Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
A change-data-capture stream delivers rows in batches. Each change carries the id it affects, the version it represents, and an op of upsert or delete. Delivery is at-least-once and unordered, so a batch can contain several versions of one id in any order, and the same batch can be delivered twice.
Write apply_batch(snapshot, changes). Return the new snapshot.
Function to write
apply_batch(snapshot: list[dict], changes: list[dict]) -> list[dict]The merged snapshot as a list of row dicts, sorted by id.
How to approach it
Two passes: pick the winning change per id, then decide whether it beats the snapshot.
Sample cases
+ 2 held back until you submit
a mixed batch
Out-of-order versions, a stale replay, a delete that wins, a new row, and a delete for a row that is not there.
Input
Argument 1
| id | version | status | plan |
|---|---|---|---|
| a | 3 | active | pro |
| b | 1 | active | free |
| c | 5 | paused | pro |
| d | 2 | active | free |
Argument 2
[
{
'id': 'a',
'version': 5,
'op': 'upsert',
'status': 'churned',
'plan': 'pro'
},
{
'id': 'a',
'version': 4,
'op': 'upsert',
'status': 'active',
'plan': 'enterprise'
},
{
'id': 'c',
'version': 2,
'op': 'upsert',
'status': 'active',
'plan': 'free'
},
{
'id': 'b',
'version': 3,
'op': 'delete'
},
{
'id': 'b',
'version': 2,
'op': 'upsert',
'status': 'paused',
'plan': 'free'
},
{
'id': 'e',
'version': 1,
'op': 'upsert',
'status': 'active',
'plan': 'free'
},
{
'id': 'z',
'version': 9,
'op': 'delete'
}
]Returns
| id | version | status | plan |
|---|---|---|---|
| a | 5 | churned | pro |
| c | 5 | paused | pro |
| d | 2 | active | free |
| e | 1 | active | free |
an empty batch
Nothing to apply, so the snapshot comes back exactly as it went in.
Input
Argument 1
| id | version | status | plan |
|---|---|---|---|
| a | 3 | active | pro |
| b | 1 | active | free |
| c | 5 | paused | pro |
| d | 2 | active | free |
Argument 2
[] (empty list)
Returns
| id | version | status | plan |
|---|---|---|---|
| a | 3 | active | pro |
| b | 1 | active | free |
| c | 5 | paused | pro |
| d | 2 | active | free |
delete then recreate
A row deleted at v2 and recreated at v3 is present, because v3 is the winner.
Input
Argument 1
| id | version | status |
|---|---|---|
| a | 1 | active |
Argument 2
[
{
'id': 'a',
'version': 2,
'op': 'delete'
},
{
'id': 'a',
'version': 3,
'op': 'upsert',
'status': 'active'
}
]Returns
| id | version | status |
|---|---|---|
| a | 3 | active |
Constraints
version decides, never position in the list. The highest version for an id is the only change that matters.delete removes the row. A winning upsert replaces it entirely with the change's fields, minus op.id. Do not modify snapshot or changes.Worked example
Id a is at version 3 in the snapshot. The batch lists v5 (churned) before v4 (enterprise). Applying in list order leaves a at v4 with plan enterprise — a row that is both stale and wrong. Choosing the highest version first leaves it at v5, churned.
Id b is the mirror image: the delete at v3 is listed before an upsert at v2. Any loop that applies changes as they come resurrects b. And id c is at v5 already, so the v2 change in the batch is a replay that must leave it untouched — the rule that makes re-running the job safe.
What this tests
Idempotent merge semantics: separating which change wins from whether it should be applied at all. Both halves are needed, and getting only one produces a pipeline that is correct until the day a batch is redelivered.
apply_batch(snapshot: list[dict], changes: list[dict]) -> list[dict]Submit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.