Trello webhook delivery ordering guarantees absent in repeatable environments
0 reputation · 25 Apr 2024, 07:06 UTC
0 reputation · 25 Apr 2024, 07:06 UTC
Teams building repeatable development environments for Trello integrations often rely on webhook subscriptions to synchronize board state across CI/CD pipelines. The Trello API documentation states that webhook events may be delayed, duplicated, or delivered out of sequence, leaving developers responsible for implementing idempotency and ordering logic.
When a rapid series of actions occurs on a card—such as adding a comment, moving the card, and changing a label—the corresponding webhook payloads can arrive in a different order than the actions were performed. This behavior introduces non-determinism into automated test suites that assert on board state after webhook triggers, causing flaky failures that are difficult to reproduce locally.
GET /1/actions endpoint provides authoritative ordering but requires an additional round trip per event.{"error": "rate limit exceeded"}) constrain how frequently the REST API can be polled for verification.What patterns have proven effective for reconstructing authoritative event order from unordered webhook deliveries? How should deduplication keys be derived when the same action may be delivered multiple times with identical payloads? Is there a recommended strategy for bounding the verification window against the REST API without exceeding rate limits?
32275 reputation · 25 Apr 2024, 09:45 UTC
To make a repeatable test environment you must treat the webhook as a fire‑and‑forget event stream and rely on Trello’s REST API for the authoritative order. The workflow is:
action.id in a local store (DB, Redis, or a file).GET /1/actions?since=&limit=1000 for the board.action.date and action.id as a tie‑breaker.action.ids are present or a timeout expires.Trello’s webhooks are intentionally unordered. The only stable identifiers are action.id and action.date. The date field has second granularity, so two actions in the same second can still arrive out of order. Duplicate deliveries are common; deduplication must be performed on action.id.
action.id (globally unique) and action.date (ISO‑8601, second resolution).date ascending.Retry-After header.since cursor that is the last confirmed action.date + 1 ms avoids re‑fetching the same action.// pseudocode
function handleWebhook(req) {
const payload = req.body;
const id = payload.action.id;
if (!store.has(id)) {
store.set(id, payload);
}
}
async function syncBoard(boardId) {
let since = lastSyncedDate[boardId] || '1970-01-01T00:00:00Z';
const res = await fetch(`https://api.trello.com/1/boards/${boardId}/actions?since=${since}&limit=1000`, {
headers: { Authorization: `Bearer ${TOKEN}` }
});
if (res.status === 429) {
const wait = res.headers.get('Retry-After') || 1;
await sleep(wait * 1000 + Math.random() * 100);
return syncBoard(boardId); // retry
}
const actions = await res.json();
actions.forEach(a => store.set(a.id, a));
if (actions.length) {
lastSyncedDate[boardId] = actions[actions.length - 1].date;
}
setTimeout(() => syncBoard(boardId), 2000); // 2‑second cadence
}
async function assertOrder(expectedIds) {
const timeout = Date.now() + 30000; // 30 s
while (Date.now() < timeout) {
const ids = store.getOrderedIds(); // sorted by date
if (expectedIds.every((id, i) => ids[i] === id)) {
return true;
}
await sleep(500);
}
throw new Error('Timeout waiting for expected actions');
}
Retry-After header and exponential backoff (max 60 s) when a 429 is received.since cursor so that a retry resumes without re‑fetching already seen actions.To fine‑tune the sync cadence for your specific test workload, could you share the average number of actions per board per minute that your CI pipeline generates?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.