The short answer: saving items and scanning exact codes do not care how big the catalogue is. Looking things up does. At 3,275,088 products, searching by code text takes 460 milliseconds per screen, and re-measured three times over longer windows it is still about 12 times slower than at 100,000 products (37 milliseconds). The record list and running totals barely move. That points at how the product search works, not at the app or the machine running out of room.
Largest catalogue tested
3,275,088
1,642 MiB of product rows actually on disk
Product cases measured
103
in 45 minutes, across 2 runs
Slowest screen we found
460 milliseconds
searching by code text at the largest catalogue
Records written and checked
3,064,085
every one reconciled against the database
Every wait below is "9 out of 10", the wait that nine people out of ten experienced, so one unusually slow moment does not distort the picture. Where something was measured three times, the slowest of the three is shown.
1. Looking products up
This is where a big catalogue bites. Change what you search for and how many products the shop carries.
What is the user doing?
How many products does the catalogue hold?
Screens that got measurably slower as the catalogue grew, worst first:
| What the user does | Smallest size measured | Wait | Largest size measured | Wait | Change | Checked again separately |
|---|---|---|---|---|---|---|
| Opening the product list | 1,000 | 3 milliseconds | 3,275,088 | 504 milliseconds | 168× | 100,000 → 3,275,088: 19× 3 repeats, 30s windows |
| Searching by barcode | 1,000 | 2 milliseconds | 3,275,088 | 291 milliseconds | 146× | 100,000 → 3,275,088: 13× 3 repeats, 30s windows |
| Searching by code text | 1,000 | 2 milliseconds | 3,275,088 | 460 milliseconds | 230× | 100,000 → 3,275,088: 12× 3 repeats, 30s windows |
Screens tested at the same record counts that stayed flat:
| What the user does | Smallest size measured | Wait | Largest size measured | Wait | Change | Checked again separately |
|---|---|---|---|---|---|---|
| Scanning one exact code | 1,000 | 2 milliseconds | 3,275,088 | 2 milliseconds | 1× | not re-measured |
| Scrolling the record list | 1,000 | 28 milliseconds | 3,275,088 | 33 milliseconds | 1.2× | not re-measured |
| The "needs review" list | 1,000 | 111 milliseconds | 3,275,088 | 113 milliseconds | 1× | not re-measured |
| Running the totals | 1,000 | 98 milliseconds | 3,275,088 | 110 milliseconds | 1.1× | not re-measured |
Each row compares sizes that operation actually ran, inside one run. The first pass (730e6854) used short windows so it could sweep every size quickly; the searches that looked slow were then re-measured (8cc71664) with longer windows and three repeats each, shown in the last column. A small catalogue makes the first factor look larger simply because the denominator is tiny — 1,000 products is a toy catalogue, not a small real shop, which is why the last column is the fairer comparison.
Worth knowing: these searches look for a piece of text anywhere inside a barcode, a code or a name, not for something that starts with it. The database cannot use an index for that, so it has to look through every product row. Exact scanning by a normalised code is different: it uses an index, which is why scanning one code stayed at 2 milliseconds even with 3,275,088 products. The fix, if we want one, is in how searching works — not in buying a bigger machine.
2. Saving items
The other half of the job: a scanner recording what it sees.
How many records does each save carry?
Does the device send a catalogue hint?
How many people are saving at the same time?
A bigger catalogue did not slow saving down. With 500 records per save and no catalogue hint, 4 people confirmed about 16,167 records a second at 3,275,088 products, against 17,250 a second at 1,000 — within 6% of the small catalogue. The wait for one save moved from 125 milliseconds to 134 milliseconds.
3. A second finding, unrelated to catalogue size
Sending a catalogue hint makes big saves much slower. At 3,275,088 products, saving 500 records with no hint finished in 50 milliseconds; the same save with a hint of the current catalogue version took 282 milliseconds, about 5.6 times longer, and confirmed far fewer records per second (11,833 against 1,917).
This is not a big-catalogue problem: it shows up at every catalogue size we tested, small ones included. Reading the server's own SQL counters, a hinted save asks the database about the catalogue version and the individual product once per record, even though the products were already loaded once for the whole batch. On a 500-record save that is a thousand extra round trips. This is a code change worth making, and it is separate from the search cost above.
4. What we actually changed between sizes
The catalogue was genuinely reduced, not faked: rows were deleted for that customer only and the table was rebuilt, so the database really had less to read. Sizes below were counted on disk after each rebuild.
| Products in the catalogue | Disk used by product rows | Compared with the step below |
|---|---|---|
| 1,000 | 3.4 MiB | - |
| 100,000 | 54.6 MiB | 16.1× |
| 1,000,000 | 507.5 MiB | 9.3× |
| 3,275,088 | 1,642 MiB | 3.2× |
The full table holds other customers' products too; only the tested customer's rows were reduced, so the page's disk figures are the product rows for that customer.
5. What we have not checked yet
Being straight about the edges of this test, because they affect what we should not promise yet.
- Searches over a big spread of codes. Every request here asked about the same product codes over and over. Real users look at different products, which can be slower, not faster.
- Many people searching at once. Product screens were measured one user at a time. Managers viewing together were not tested at scale.
- Deep pages and long lists. Only the first page of results was measured.
- Jobs with far more records. The record list, review filter and totals were measured with 100,000 records already counted, not millions.
- Expected-stock comparisons. The comparison screen was run with no expected quantities at all, so it only shows the counting side.
- How we get those 3,275,088 products there. Importing a catalogue file, building the offline catalogue on a handset, and generating reports were not measured.
- A full working day, and a cold start. Short bursts on a machine that was already warm; not a long shift or a cold database.
- Our production cloud setup. These numbers come from our own laptop, not the servers customers use.
6. How to trust this
- Nothing was taken on trust. After every single test we went back to the stored data and counted what was really written: totals, quantity, and that each record appeared exactly once with the code, device and sequence number we sent. All 103 checks passed and nothing failed.
- The size comparison is fair by construction. At each catalogue size the app was restarted against a fresh copy of the same database, the same queries ran in the same order, and the same shared set of product codes was used — so we are not comparing "a small table" against "the same table plus a hot spot".
- Slow results were re-measured. The searches that looked bad were run three separate times at the largest size. All three agreed, which is why the page only calls a slowdown confirmed when it repeats.
- The write speed is a closed loop, not a real shift. Save requests were fired back to back with no pause, and the numbers include the checking our harness does. A scanner pauses between items.
- Where the time goes comes from the database itself, not from our estimates: the figures under "database busy" are the PostgreSQL container's own CPU accounting, and there were no memory-pressure slowdowns during any run.
- Our development data was never touched. Every write happened in a separate copy created for the test and deleted afterwards. The original catalogue is still exactly 3,275,088 products.
Runs: 730e6854, 8cc71664 · version 20261009-products · generated from the measured result files in tests/performance/20261009-products/reports/. Every figure on this page comes from those files; none are typed in by hand.