Object storage is usually specified in gigabytes per second, which suits the workloads it was first adopted for. Backup streams and media archives move large objects and care about bandwidth. A growing share of what object storage now serves does not look like that at all: analytics engines, container registries, AI data loaders and application back ends issue enormous numbers of small requests, and for them the useful measure is requests per second and the latency of each one.
A system that satisfies a bandwidth requirement comfortably can fail a request rate requirement badly, and the two are not related closely enough to infer one from the other. Establishing which one the workload needs is the first step in sizing, and it is often skipped because bandwidth is the number vendors publish.
Throughput asks how much data can move per second. Request rate asks how many separate operations can be served per second. Moving 10 GB as a single object and moving it as 200,000 small objects place completely different demands on the system, even though the volume is identical.
The reason is that every request carries fixed cost regardless of size: authentication, request parsing, a metadata lookup to locate the object, and the response. For a large object that overhead is negligible against the transfer. For a 40 KB object it can dominate entirely. A system serving small objects spends most of its effort on the overhead, which is why its bandwidth graph can look almost idle while it is completely saturated.
Latency is the third dimension and it matters whenever an application waits. A batch job that issues requests in parallel can tolerate latency by keeping many in flight. An application that must fetch an object before it can respond to a user cannot.
Understanding which part of a request is expensive helps in deciding what to change.
| Stage | Cost driver | What reduces it |
|---|---|---|
| Connection establishment | New connections and encryption handshakes | Connection reuse and pooling in the client |
| Authentication | Signature calculation per request | Efficient client libraries, reused sessions |
| Metadata lookup | Locating the object in the namespace | Key naming that spreads load, adequate metadata resources |
| Data retrieval | Reading from the underlying media | Caching, media choice, larger objects |
| Listing operations | Scanning many keys under a prefix | Avoiding listing in hot paths, using an external index |
| Response and teardown | Per-request processing on both ends | Higher concurrency, fewer and larger requests |
Listing deserves separate mention because it is the operation most likely to surprise. An application that lists a prefix before fetching an object is performing a much more expensive operation than the fetch itself, and in a bucket with millions of keys the cost grows with the number of keys rather than with the number needed. Replacing that pattern with a maintained index outside the object store is often the single largest improvement available.
The inputs are straightforward to gather. Measure the object size distribution rather than the average, since a mean of 4 MB can describe a set that is mostly 20 KB objects with a few very large ones. Count operations per second at peak, separated by type, because a list, a small get and a large put are different costs. And establish what latency the application needs, in particular whether anything is waiting synchronously.
With those three, the sizing conversation changes. A workload dominated by small gets is asking for request handling capacity and low latency metadata, and adding network bandwidth will not help it. A workload of large sequential transfers is asking for bandwidth, and request handling is not the constraint. Most real environments contain both, which means both have to be sized rather than the larger number being assumed to cover the smaller.
This is also what makes capacity sizing incomplete on its own. Two deployments holding the same number of petabytes can need very different configurations depending on how many objects those petabytes are divided into.
On the client side, concurrency is the first lever. A single threaded application issuing one request at a time will see latency multiplied by request count no matter how capable the storage is. Raising concurrency and reusing connections frequently produces a large improvement at no cost.
On the data side, object size is the structural lever. Combining many small objects into larger ones reduces request count proportionally, which is why packed formats are common in analytics and machine learning. It trades flexibility for efficiency, since individual items can no longer be updated or deleted independently, and that trade should be made deliberately rather than by default.
Key naming matters in systems that distribute by key, since keys sharing a long common prefix can concentrate load on a subset of the system. Introducing variation early in the key spreads requests more evenly. It is worth confirming how a specific platform distributes before redesigning key names, because the behavior differs between implementations.
Scality RING distributes both data and metadata across nodes, so request handling capacity grows as the system grows rather than being fixed by a controller pair. For workloads dominated by small objects, that scaling of metadata and request processing is what determines the achievable rate, and it is the figure worth asking about in an evaluation.
In practice the useful test is aggregate requests per second at the object size distribution the workload actually has, with the object count the namespace will actually reach, measured under concurrent access from the real number of clients. A bandwidth figure taken with large objects will not predict it, and a request rate measured on an almost empty namespace will not predict behavior once the bucket holds hundreds of millions of keys.
The practical recommendation is to record three figures for any object storage environment: sustained throughput with large objects, sustained request rate with the real small object profile, and latency at the percentiles the application cares about. Any one of them alone gives an incomplete picture, and the third is the one applications experience.
Take them again as the environment grows. Request rate behavior changes with object count in ways that throughput does not, so a system that performed well at 50 million objects deserves remeasuring at 500 million. That is the point at which many applications start reporting timeouts that look like application errors rather than a storage characteristic.