# Cube Runner

> Plan differences, block sources, the long-term storage queue and history depth for getBlock requests to constant-k Solana RPC endpoints.

Source: https://docs.constant-k.com/cube-runner/

Cube Runner answers every [`getBlock`](https://docs.constant-k.com/rpc/http/getblock/) request sent to a [constant-k Solana RPC](https://docs.constant-k.com/rpc/) endpoint, in each region. It serves blocks from the region's cache, the region's ledger and the caches of other regions. On Blade Runner and Nexus tier plans, it also retrieves blocks from long-term storage, which holds every block since genesis.

There is no separate endpoint and no client change. Parameters and responses are those of standard `getBlock`, and the response is byte-identical whichever source serves it.

## Plan comparison

|                                                                               | Free, Operator                                                                                                                                                     |      Blade Runner      |          Nexus         |
| :---------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------: | :--------------------: |
| [`getFirstAvailableBlock`](https://docs.constant-k.com/rpc/http/getfirstavailableblock/) returns         | The oldest block from which the region holds every later block, in its cache or its ledger. Older blocks are still returned when a cache in any region holds them. | `0`, the genesis block | `0`, the genesis block |
| Retrieval rate from long-term storage, per region                             | Not included                                                                                                                                                       | 1 block per 15 seconds |  1 block per 3 seconds |
| Monthly allowance per API key, per region                                     | Not applicable                                                                                                                                                     |      10,000 blocks     |      50,000 blocks     |
| Blocks queued per API key                                                     | Not applicable                                                                                                                                                     |           200          |           500          |
| Blocks queued per region                                                      | Not applicable                                                                                                                                                     |          1,000         |          2,000         |
| Longest allowed queue time (no guaranteed delivery; request again to requeue) | Not applicable                                                                                                                                                     |         1 hour         |         2 hours        |

* The monthly allowance counts only blocks retrieved from long-term storage for that API key. Blocks served by a cache, a ledger or another region never count.
* Each region keeps its own count, so an API key has the full allowance in each region.
* The allowance resets at 00:00 UTC on the first day of each month.
* The `getBlock` rate limit in [Plans & Limits](https://docs.constant-k.com/get-started/plans-and-limits/#solana-rpc-methods) applies to every `getBlock` request, whichever source serves it.

## Lookup order

Each request is answered by the first source that holds the block.

| Order | Source                         | Holds                                                                                                                                         | Plans               |
| :---: | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :------------------ |
|   1   | Region cache, memory then disk | Recent blocks, and older blocks that are requested often. The depth depends on stored data, so it varies with block size and between regions. | All                 |
|   2   | Region ledger                  | The [ledger window](https://docs.constant-k.com/rpc/#transaction-history)                                                                                                | All                 |
|   3   | Cache of another region        | Blocks in that region's cache                                                                                                                 | All                 |
|   4   | Long-term storage              | Every block since genesis                                                                                                                     | Blade Runner, Nexus |

A region asks another region only when that region holds the block. Each region also copies, in the background, the blocks it is missing from the caches of the other regions, as far back as its own cache can hold.

### Free and Operator tier plans

On Free and Operator tier plans, a `getBlock` request for a block that no cache, ledger or other region holds returns code `-32001` with HTTP `200`. The message and `data.firstAvailableBlock` carry the value [`getFirstAvailableBlock`](https://docs.constant-k.com/rpc/http/getfirstavailableblock/) returns: every block from that slot onward can be retrieved.

```json
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Block 250000000 not available on this tier. First available block: 452746590",
    "data": { "slot": 250000000, "firstAvailableBlock": 452746590, "reason": "tier" }
  },
  "id": 1
}
```

## Long-term storage

On Blade Runner and Nexus tier plans, Cube Runner retrieves a block from long-term storage when no cache, ledger or other region holds it. Each plan has its own queue in each region, shared by every API key on that plan in that region. The retrieval rate and queue limits for each plan are in [Plan comparison](#plan-comparison).

* A block requested by several API keys at once is retrieved once.
* A retrieved block is held in the region for at least 15 minutes and served from there to any API key on any plan. More recent blocks are held longer.
* Blocks that are requested often are kept longer. When space is needed, the oldest blocks leave first, but a block requested several times in the last few hours stays until requests for it stop.

### Queued requests

| Block                                      | Response                                                                          |
| :----------------------------------------- | :-------------------------------------------------------------------------------- |
| Retrieved within 25 seconds of the request | The block, HTTP `200`                                                             |
| Not retrieved within 25 seconds            | HTTP `429` with a `Retry-After` header and code `-32019`. The block stays queued. |

```json
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32019,
    "message": "Block 250000000 is being retrieved from long-term storage; retry in 30 seconds",
    "data": { "slot": 250000000, "retryAfterSecs": 30, "queuePosition": 2 }
  },
  "id": 1
}
```

* `Retry-After` is between 1 and 60 seconds.
* Repeating the request after `Retry-After` returns the block once it has been retrieved.
* A queued block is dropped when no request for it arrives for 150 seconds. The next request queues it again.

### Responses that are not queued

When a block was not queued, was dropped from the queue, or was not returned by long-term storage, the response also carries HTTP `429`, a `Retry-After` header and code `-32019`. `data.reason` names the cause.

| `data.reason` | Cause                                                                                                                                                        |
| :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queue_full`  | The region's queue for the plan is full. The block was not queued.                                                                                           |
| `key_full`    | The API key already has the plan's number of blocks queued: 200 on Blade Runner, 500 on Nexus. The block was not queued.                                     |
| `key_monthly` | The API key has used its monthly allowance in the region. `Retry-After` is 60. The allowance resets at the start of the next month.                          |
| `dropped`     | The queued block reached the plan's longest allowed queue time before it was retrieved. Delivery is not guaranteed; the next request queues the block again. |
| `failed`      | Long-term storage did not return the block.                                                                                                                  |

### Client handling

1. On HTTP `429` with code `-32019`, wait the number of seconds in `Retry-After`, then send the same request again.
2. Repeat each queued request within 150 seconds, or the block is dropped from the queue.
3. Treat only `-32007` and `-32009` as skipped slots. Code `-32019` and HTTP `429` mean the block has not been served yet.

| Client                             | Behaviour on HTTP `429`                                                                                                    |
| :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
| `solana-client` `RpcClient` (Rust) | Retries up to 5 times and waits for `Retry-After` when it is under 120 seconds. The default request timeout is 30 seconds. |
| `@solana/web3.js` 1.x `Connection` | Retries 4 times with its own backoff of 0.5 to 4 seconds. Ignores `Retry-After`.                                           |
| `@solana/kit`                      | Does not retry. The error carries the response headers.                                                                    |

### Retrieval order

1. API keys on the plan take turns.
2. On an API key's turn, Cube Runner retrieves the key's queued block that the most API keys are waiting for. Repeated requests from one API key do not add to that count.
3. Among blocks with the same count, the newest block is retrieved first.

An API key gets fewer turns as its [repeat count](#repeat-requests) grows.

## Repeat requests

Cube Runner records which blocks each API key received in the last 24 hours. Requesting a block again costs nothing while a cache holds it. An API key that makes Cube Runner retrieve a block again from long-term storage, after it already received that block, builds up a repeat count, which reduces its turns in the long-term storage queue.

| Rule                      | Value                                                                                                                         |
| :------------------------ | :---------------------------------------------------------------------------------------------------------------------------- |
| Requests that never count | Every request served by a cache, a ledger or another region, however often the same block is requested                        |
| A request that counts     | One that retrieves again from long-term storage a block the API key received in the last 24 hours: adds 2 to the repeat count |
| Decay                     | The repeat count halves every 6 hours                                                                                         |

| Repeat count | Turns in the long-term storage queue, compared with an API key whose repeat count is 0 |
| :----------: | :------------------------------------------------------------------------------------- |
|      50      | Half as many                                                                           |
|      150     | A quarter as many                                                                      |

The repeat count affects only these turns. Requests served by a cache, a ledger or another region are never delayed by it.

## History depth

| Method                                                        | Behaviour                                                                                                                                                  |
| :------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`getFirstAvailableBlock`](https://docs.constant-k.com/rpc/http/getfirstavailableblock/) | Returns the oldest block from which every later block can be retrieved with `getBlock`. The value for each plan is in [Plan comparison](#plan-comparison). |
| [`minimumLedgerSlot`](https://docs.constant-k.com/rpc/http/minimumledgerslot/)           | Returns the oldest slot the region's ledger holds. That slot is the history depth for `getTransaction` and `getSignaturesForAddress`.                      |
| `getTransaction`, `getSignaturesForAddress`                   | Not served by Cube Runner. Their history ends at the region's [ledger window](https://docs.constant-k.com/rpc/#transaction-history).                                                  |

## Related

- [getBlock](https://docs.constant-k.com/rpc/http/getblock/)

- [Transaction history](https://docs.constant-k.com/rpc/#transaction-history)

- [Errors](https://docs.constant-k.com/rpc/errors/)

- [Plans & Limits](https://docs.constant-k.com/get-started/plans-and-limits/)
