Log-log chart: a dotted extrapolation line ending near 10 ms, two measured no-index curves far above it (MariaDB 10.11 at 968 ms and 13.0 at 148 ms at 1M rows), a dashed 300 ms threshold, and a flat under-1 ms line for the indexed case

I Wrote the Index Escalation Threshold as a Number, Then Measured It and Found It Off by 15x to 100x

Somewhere in a design document you have signed off on, there is a sentence of the form “when it passes X, we will do Y.” Has anyone ever generated X rows to check that the number means what it says? At the end of last month I signed off on a design that runs a lookup against a column with no index, on a table my team does not own, and wrote the condition for fixing it as a sentence with two numbers in it: “when the table passes about 1,000,000 rows or the p95 of the voucher filter passes 300 ms, request a non-unique index from the owning team.” The sentence had one job: get the option “ship without the index” through design review, with “we will add it later” turned into something a reviewer could sign. It did that job. ...

September 28, 2026 · 10 min · Hoang Nguyen Thai
A 200-unit bar split into 110 allocated and 90 shared pool, the 10 units sold out of quotas highlighted inside the 110, and two subtractions below it: the literal reading giving 80 and the production screen giving 90

The Formula in the Ticket Subtracted Sales Twice, and Only Two Screenshots Could Prove It

One SKU. Total intake 200 units. Three campaigns hold quotas of 10, 88 and 12 units, and have sold 1, 9 and 0 out of those quotas. How many units are still available to sell? The ticket said: available = total intake − total allocated − total sold. Plug the numbers in and you get 200 − 110 − 10 = 80. The production screen for that SKU, at the same moment, said 90. ...

September 23, 2026 · 7 min · Hoang Nguyen Thai
Two tables with overlapping auto-increment ids, an import file pointing at one and the code reading the other

The Import That Edited the Wrong Row for Years Because Two Tables Shared the Same IDs

An import feature that adjusts campaign quotas from a spreadsheet had been in production for years. Every manual QA pass looked the same: upload a file, open the campaign, see one quota changed, mark the ticket done. Zero automated tests covered the handler. It was editing the wrong row. Not sometimes — structurally, on every run where the two ids happened to line up, and on the staging database they did. The number of production rows it touched over those years is something I have not measured and will not guess at here. What I can show is the mechanism, why three separate safety nets each let it through, and the one test that would have caught it on day one. ...

September 22, 2026 · 7 min · Hoang Nguyen Thai
An orphaned row still counted against a shared stock pool with no screen to release it

Ghost Quota: How a Missing Foreign Key Quietly Lowers a Sales Ceiling

A table called campaign_product_variants had no foreign key, no ON DELETE CASCADE, and no deleted_at. Before the shared pool existed, that was a cosmetic problem: a handful of rows pointing at SKUs that no longer existed made a report look a few units smaller than reality. I never saw anyone treat that as a bug, and I did not go back through old tickets to check. Then we shipped a shared stock pool — several campaigns drawing quota from one pool per SKU — and the same orphaned rows stopped being cosmetic. On a dev database I could still poke at, 14 orphan rows were locking 294 units out of sale, permanently, with no admin screen that could select and release them. The missing foreign key had turned pre-existing technical debt into a lowered sales ceiling. ...

September 22, 2026 · 5 min · Hoang Nguyen Thai
UUIDv7 Structure and B-Tree Performance

UUIDv7: Why You Should Stop Using Auto-increment Integers and UUIDv4

The Challenge of Identifiers In software engineering, choosing a Primary Key type seems simple, but it profoundly affects both security and system performance as you scale. We typically start with two familiar choices: Auto-increment Integers or UUIDv4. 1. Limitations of Traditional Methods Option 1: Auto-increment Integer -- Example: Sequential IDs over time 1, 2, 3, 4, 5... Pros: Extremely fast and memory-efficient. Cons: Lack of security. If your URL is https://api.myapp.com/v1/invoice/1001, a competitor can easily guess that the next invoice is 1002, or even count your total customer base just by incrementing these numbers. Option 2: UUIDv4 (Random String) -- Example: No discernible order f47ac10b-58cc-4372-a567-0e02b2c3d479 0e58c3d4-a567-4372-58cc-f47ac10b0e02 Pros: Maximum security; impossible to predict. Cons: Database Fragmentation. Because it is entirely random, the Database must work hard to sort these into memory, slowing down the system as your data grows. 2. UUIDv7: The Best of Both Worlds UUIDv7 solves these problems by combining: [Current Timestamp] + [Random Bits]. ...

March 9, 2026 · 3 min · Hoang Nguyen Thai