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

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)
