Retail — online marketplace

Terabytes In, 20 Milliseconds Out: Real-Time Serviceability at Checkout

Deciding whether a product can actually reach a customer's pincode means computing, per product, which fulfilment location can serve it and whether a logistics partner covers that lane — over terabytes of continuously ingested store, stock and logistics data. It runs on every listing page, product page and checkout. We designed it as an Apache Ignite data grid fed by an 83-processor NiFi ingestion pipeline over Kafka, fronted by a batch-capable service API, taking it from roughly 3 to 3,000 transactions per second and from around 3 seconds to 20 milliseconds.

Client
Top-3 Indian fashion & lifestyle marketplace
Role
Lead architect — solution design, data ingestion, performance engineering and go-live recovery
Period
2018-07 – 2020-02
3 → 3,000transactions per secondon the checkout critical path
3s → 20msresponse timeroughly a 150× reduction
~100 GBin-memory working setwith native persistence
18 Feb 2020live in productionafter an earlier attempt failed

What serviceability actually is

On an Indian marketplace, "can I get this?" is not a property of a product. It is the answer to a calculation performed across four moving datasets at once: which sellers or stores hold this item, which of them currently has stock, whether any logistics partner runs a lane from that specific location to this specific pincode, and which of the resulting combinations can still meet the delivery promise being shown on the page.

None of those inputs are stable. Stock moves continuously. Sellers are onboarded and suspended. Logistics partners revise their coverage matrices — which pincodes they serve, for which shipment classes, under what conditions — on their own schedule, and deliver those revisions as bulk files measured in gigabytes. The full input estate runs to terabytes.

The output is needed in the three most expensive places on the site. Every tile on a product listing page needs it, so a single page render triggers dozens of these calculations. Every product page needs it. Every checkout needs it, and there it must be right, because a wrong answer at checkout becomes an order the business cannot fulfil.

Why the platform-native implementation could not carry it

Built against the commerce platform's own persistence, serviceability sustained single-digit transactions per second at multi-second response times. That is not a tuning gap; it is a structural one, and it put a ceiling on the whole storefront rather than on one feature.

The reason is that this is a computation, not a lookup — and the two have opposite caching characteristics. A lookup has a stable answer you can cache and invalidate. This has an answer assembled at request time from four datasets, any of which may have changed in the last minute. Every caching strategy tried against it had eventually failed for the same reason: the cache was correct when written and wrong when read.

The dimensionality rules out the obvious alternative. India has tens of thousands of destination pincodes; the marketplace had thousands of fulfilment locations and a large SKU base. The cross product is not merely expensive to precompute, it is unbounded in any useful sense. But pure on-demand computation cannot meet a millisecond budget either. Neither pure strategy works, which is why the problem had survived more than one attempt.

It looks like a yes/no flag. It is a four-way join over terabytes, on the checkout path, in milliseconds.

Splitting the problem by volatility

The design decision that made this tractable was to stop splitting the problem by entity — stock here, logistics there — and split it by volatility instead.

Some dimensions are stable over hours or days: a logistics partner's coverage of a pincode, the set of lanes it operates, the service classes it supports. Those can be collapsed ahead of time into a resolved structure. Other dimensions change by the minute: stock, seller status, price-linked eligibility. Those must be evaluated at request time.

Once the stable dimensions are pre-resolved, the live calculation is small enough to fit inside a millisecond budget. Establishing exactly where that line falls is the analytical work — and it has to be revisited whenever a feed's refresh characteristics change.

The architecture

Serviceability moved off the commerce platform onto an Apache Ignite data grid, sized in the order of 100 GB with native persistence enabled, fronted by a dedicated Spring MVC service. Data and computation are co-located in the grid, so resolving a candidate set does not cost a network round trip per candidate — which is what makes the difference between 20 milliseconds and 3 seconds.

Feeds arrive through an Apache NiFi pipeline running 83 processors in production: bulk file ingest and HTTP listeners at the edge, record-oriented conversion, JOLT transformation to normalise partner formats that agree on nothing, and merge and split stages that batch records before publishing to Kafka. The grid consumes from Kafka. Nothing in the request path waits on a source system.

The service contract has two calls rather than one. The first resolves serviceability for a batch of items against a pincode and returns the fulfilling location; the second, called only when needed, estimates the delivery date for a chosen location and logistics partner. Splitting them means the common question is answered without paying for the expensive one — and the batch shape means a listing page costs one request rather than one per tile.

Landing a launch that had already failed

The programme carried a failed go-live from roughly a year earlier, a deadline, and an unresolved disagreement about the datastore. An interim fallback had been chosen, but it had not been load-tested at production request volume and functional testing against it began days before the launch window — so the risk had been moved rather than removed.

Three things got it over the line. The primary path and the fallback were separated so each could be tested, measured and failed independently. Order-management node discovery was changed and sequenced ahead of the launch rather than alongside it, so two moving parts did not land in the same window. And a war room ran with client, systems-integrator and platform teams on one bridge with a single decision owner — not a standing status call, but a room with the authority to make the go/no-go.

The feature reached production on 18 February 2020. Post-release data inconsistencies were resolved against live data without a rollback, and the monitoring and support plan was handed to the client's own team at go-live rather than after stabilisation.

What I would tell someone facing this

  • Establish whether you have a lookup or a calculation before you choose a caching strategy. Caching a calculation whose inputs change continuously is how teams lose quarters.
  • Split by volatility, not by entity. The useful question is which dimensions are stable enough to pre-resolve — not which system owns which table.
  • Budget for listing-page fan-out, not for a single request. One render can trigger dozens of calls; a per-request budget that looks comfortable will not survive contact with a category page.
  • Do not run your hottest calculation through your commerce platform's persistence layer. That is an architecture problem, and no amount of tuning fixes it.
  • Load-test the fallback, not just the primary. An untested fallback is a second outage waiting for the first one.
  • A launch that has already failed once needs a decision owner on the bridge, not another status meeting.

Architecture

NiFi ingestion flow publishing records to Kafka
Serviceability inputs are ingested continuously rather than pulled on demand: files are picked up, schema-tagged, converted to Avro and published to Kafka, which feeds the in-memory grid.

In their words

As you may have known, we are live with the pincode release and I cannot emphasise enough the importance of the contribution to this release. Thanks for spending nights in our office and owning up this piece. You have been fantastic throughout.

Programme leadership, client side · 2020-02

Played the most important role in this release. Not only worked very hard but also found innovative ways to solve some of the post-release data issues.

Delivery leadership, client side · 2020-02

Technology

  • Apache Ignite 2.7.5
  • Apache NiFi
  • Apache Kafka 2.2.1
  • Spring MVC / Spring Integration
  • MongoDB (interim fallback)
  • SAP Commerce (hybris)
  • Order Management
  • Gatling (load testing)

Want the working artefacts?

The account above is the whole story. If you want the material behind it — data models and partitioning, sizing inputs, load-test design, the decision frameworks as something you can actually apply — we will send the appendix for Terabytes In, 20 Milliseconds Out: Real-Time Serviceability at Checkout to a work address. Client identities stay masked either way.

Facing something similar?

If any of this maps onto a problem you are carrying, we are happy to talk it through — no obligation, and you will get a straight answer about whether it is worth doing.

Start a conversation