Skip to content
Notifications
Clear all

My workflow: How we manage exception requests without chaos

2 Posts
2 Users
0 Reactions
1 Views
(@backend_latency_queen)
Reputable Member
Joined: 2 months ago
Posts: 159
Topic starter   [#13700]

Managing exception requests—like one-off data corrections or overriding a validation rule—is a notorious source of chaos. It often starts with a Slack message, escalates to a database `UPDATE` run directly in production, and leaves no audit trail. Our team has built a workflow using a simple internal service that brings order to this process, heavily leaning on idempotency and state machines.

At its core, the service is a Go service with a Postgres backend and Redis for caching request state. We treat each exception as a "ticket" with a strict lifecycle. The key was designing a schema that captures intent, approval, execution, and result.

Here's the simplified core of our `exception_requests` table:

```sql
CREATE TABLE exception_requests (
id UUID PRIMARY KEY,
request_type VARCHAR(50) NOT NULL, -- e.g., 'user_data_correction'
entity_id VARCHAR(100) NOT NULL, -- The affected record
requested_by VARCHAR(100) NOT NULL,
requested_at TIMESTAMPTZ NOT NULL,
payload JSONB NOT NULL, -- The actual change
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, approved, rejected, executed, failed
approved_by VARCHAR(100),
approved_at TIMESTAMPTZ,
execution_result JSONB,
idempotency_key VARCHAR(255) UNIQUE -- Critical for retries
);
```

The workflow:

* **Submission:** An engineer POSTs to our internal API with an `idempotency_key`. This prevents duplicate requests from being processed.
* **Approval:** A lead reviews a queue (a simple UI querying this table). Approval updates `status`, `approved_by`, and `approved_at`.
* **Execution:** A separate, idempotent worker process picks up `approved` records. It executes the logic specific to the `request_type` against our services.
* **Result:** The `execution_result` and `status` are updated. We cache the final state in Redis for 24 hours for quick lookup by other systems.

We avoid chaos by:
- Never allowing direct database writes for these; all changes funnel through this service.
- Using the `idempotency_key` to make the entire flow retry-safe.
- Logging every state transition for a clear audit trail.
- Caching the final decision in Redis to prevent repeated expensive joins for common lookup patterns (e.g., "was exception X for entity Y applied?").

The result is a system where exceptions are trackable, repeatable, and non-destructive. It's not glamorous, but it eliminated the "who changed what and why?" panic.

-- latency


sub-100ms or bust


   
Quote
(@code_reviewer_anna_v2)
Estimable Member
Joined: 3 months ago
Posts: 126
 

I love the idempotency and state machine approach. One thing I'd add is that making the `payload` field a structured JSONB is smart, but it's worth adding a validation layer that checks the payload against a JSON schema for that particular `request_type`. We learned that the hard way when someone sent a malformed payload that crashed the executor service.

We also added a `context` column for storing the original Slack message or ticket link. It's been a lifesaver for debugging "why was this approved?" months later.


Clean code, happy life


   
ReplyQuote