Add mdformat pre-commit hook
This commit is contained in:
56
api.md
56
api.md
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Note:** This is a design for the API of the write-side of a database system where writing and reading are decoupled. The read-side of the system is expected to use the `/v1/subscribe` endpoint to maintain a queryable representation of the key-value data. In other words, reading from this "database" is left as an exercise for the reader. Authentication and authorization are out of scope for this design.
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /v1/version`
|
||||
|
||||
@@ -20,16 +20,16 @@ Retrieves the latest known committed version and the current leader.
|
||||
}
|
||||
```
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `POST /v1/commit`
|
||||
|
||||
Submits a transaction to be committed. The transaction consists of read preconditions, writes, and deletes.
|
||||
|
||||
* Clients may receive a **`413 Content Too Large`** response if the request exceeds a configurable limit.
|
||||
* A malformed request will result in a **`400 Bad Request`** response.
|
||||
* Keys are sorted by a lexicographical comparison of their raw byte values.
|
||||
* All binary data for keys and values must be encoded using the standard base64 scheme defined in [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4), with padding included.
|
||||
- Clients may receive a **`413 Content Too Large`** response if the request exceeds a configurable limit.
|
||||
- A malformed request will result in a **`400 Bad Request`** response.
|
||||
- Keys are sorted by a lexicographical comparison of their raw byte values.
|
||||
- All binary data for keys and values must be encoded using the standard base64 scheme defined in [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4), with padding included.
|
||||
|
||||
### Request
|
||||
|
||||
@@ -97,18 +97,18 @@ Submits a transaction to be committed. The transaction consists of read precondi
|
||||
|
||||
### Detailed Notes for `/v1/commit`
|
||||
|
||||
1. **`request_id`**: Optional field that can be used with `/v1/status` to determine the outcome if no reply is received. If omitted, a UUID will be automatically generated by the server, and clients will not be able to determine commit status if there's no response. When provided, the request_id must meet the minimum length requirement (configurable, default 20 characters) to ensure sufficient entropy for collision avoidance. This ID must not be reused in a commit request. For idempotency, if a response is not received, the client must use `/v1/status` to determine the request's outcome. The original `request_id` should not be reused for a new commit attempt; instead, a retry should be sent with a new `request_id`. The alternative design would require the leader to store every request ID in memory.
|
||||
1. **`request_id`**: Optional field that can be used with `/v1/status` to determine the outcome if no reply is received. If omitted, a UUID will be automatically generated by the server, and clients will not be able to determine commit status if there's no response. When provided, the request_id must meet the minimum length requirement (configurable, default 20 characters) to ensure sufficient entropy for collision avoidance. This ID must not be reused in a commit request. For idempotency, if a response is not received, the client must use `/v1/status` to determine the request's outcome. The original `request_id` should not be reused for a new commit attempt; instead, a retry should be sent with a new `request_id`. The alternative design would require the leader to store every request ID in memory.
|
||||
|
||||
2. **`preconditions` (Guarantees and Usage)**: The condition is satisfied if the server verifies that the range has not changed since the specified version. Clients can achieve serializable isolation by including all reads that influenced their writes. By default, clients should assume that any read they perform influences their writes. Omitting reads is an expert-level optimization and should generally be avoided.
|
||||
1. **`preconditions` (Guarantees and Usage)**: The condition is satisfied if the server verifies that the range has not changed since the specified version. Clients can achieve serializable isolation by including all reads that influenced their writes. By default, clients should assume that any read they perform influences their writes. Omitting reads is an expert-level optimization and should generally be avoided.
|
||||
|
||||
3. **`preconditions` (False Positives & Leader Changes)**: Precondition checks are conservative and best-effort; it's possible to reject a transaction where the range hasn't actually changed. In all such cases, clients should retry with a more recent read version. Two examples of false positives are:
|
||||
1. **`preconditions` (False Positives & Leader Changes)**: Precondition checks are conservative and best-effort; it's possible to reject a transaction where the range hasn't actually changed. In all such cases, clients should retry with a more recent read version. Two examples of false positives are:
|
||||
|
||||
* **Implementation Detail:** The leader may use partitioned conflict history for performance. A conflict in one partition (even from a transaction that later aborts) can cause a rejection.
|
||||
* **Leader Changes:** A version is only valid within the term of the leader that issued it. Since conflict history is stored in memory, a leadership change invalidates all previously issued read versions. Any transaction using such a version will be rejected.
|
||||
- **Implementation Detail:** The leader may use partitioned conflict history for performance. A conflict in one partition (even from a transaction that later aborts) can cause a rejection.
|
||||
- **Leader Changes:** A version is only valid within the term of the leader that issued it. Since conflict history is stored in memory, a leadership change invalidates all previously issued read versions. Any transaction using such a version will be rejected.
|
||||
|
||||
The versions in the precondition checks need not be the same.
|
||||
The versions in the precondition checks need not be the same.
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /v1/status`
|
||||
|
||||
@@ -125,7 +125,7 @@ Gets the status of a previous commit request by its `request_id`.
|
||||
| `request_id` | string | Yes | The `request_id` from the original `/v1/commit` request. |
|
||||
| `min_version` | integer | Yes | An optimization that constrains the log scan. This value should be the latest version the client knew to be committed *before* sending the original request. |
|
||||
|
||||
> **Warning\!** If the provided `min_version` is later than the transaction's actual commit version, the server might not find the record in the scanned portion of the log. This can result in an `id_not_found` status, even if the transaction actually committed.
|
||||
> **Warning!** If the provided `min_version` is later than the transaction's actual commit version, the server might not find the record in the scanned portion of the log. This can result in an `id_not_found` status, even if the transaction actually committed.
|
||||
|
||||
### Response
|
||||
|
||||
@@ -144,7 +144,7 @@ A response from this endpoint guarantees the original request is no longer in fl
|
||||
|
||||
> **Note on `log_truncated` status:** This indicates the `request_id` log has been truncated after `min_version`, making it impossible to determine the original request's outcome. There is no way to avoid this without storing an arbitrarily large number of request IDs. Clients must treat this as an indeterminate outcome. Retrying the transaction is unsafe unless the client has an external method to verify the original transaction's status. This error should be propagated to the caller. `request_id`s are retained for a configurable minimum time and number of versions so this should be extremely rare.
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /v1/subscribe`
|
||||
|
||||
@@ -156,10 +156,10 @@ Clients should rely on the `version` field within the `transaction` and `checkpo
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| Parameter | Type | Required | Description |
|
||||
| :-------- | :------ | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `after` | integer | No | The version after which to start streaming transactions. Defaults to streaming from the latest committed version. On reconnect, clients should set this to the last version they successfully processed. |
|
||||
| `durable` | boolean | No | If `true` (the default), the stream sends `transaction` events only after they are durably committed. This increases latency but simplifies client logic. When `durable=true`, `checkpoint` events are not sent. |
|
||||
| `after` | integer | No | The version after which to start streaming transactions. Defaults to streaming from the latest committed version. On reconnect, clients should set this to the last version they successfully processed. |
|
||||
| `durable` | boolean | No | If `true` (the default), the stream sends `transaction` events only after they are durably committed. This increases latency but simplifies client logic. When `durable=true`, `checkpoint` events are not sent. |
|
||||
|
||||
### Server-Sent Events Stream
|
||||
|
||||
@@ -190,11 +190,11 @@ data: {"committed_version":123456,"leader_id":"abcdefg"}
|
||||
|
||||
### Detailed Notes for `/v1/subscribe`
|
||||
|
||||
1. **Data Guarantees**: When `durable=false`, this endpoint streams *accepted*, but not necessarily *durable/committed*, transactions. *Accepted* transactions will eventually commit unless the current leader changes.
|
||||
1. **Data Guarantees**: When `durable=false`, this endpoint streams *accepted*, but not necessarily *durable/committed*, transactions. *Accepted* transactions will eventually commit unless the current leader changes.
|
||||
|
||||
2. **Leader Changes & Reconnection**: When `durable=false`, if the leader changes, clients **must** discard all of that leader's `transaction` events received after their last-seen `checkpoint` event. They must then manually reconnect (as the server connection will likely be terminated) and restart the subscription by setting the `after` query parameter to the version specified in that last-known checkpoint. Clients should implement a randomized exponential backoff strategy (backoff with jitter) when reconnecting.
|
||||
1. **Leader Changes & Reconnection**: When `durable=false`, if the leader changes, clients **must** discard all of that leader's `transaction` events received after their last-seen `checkpoint` event. They must then manually reconnect (as the server connection will likely be terminated) and restart the subscription by setting the `after` query parameter to the version specified in that last-known checkpoint. Clients should implement a randomized exponential backoff strategy (backoff with jitter) when reconnecting.
|
||||
|
||||
3. **Connection Handling & Errors**: The server may periodically send `keepalive` comments to prevent idle timeouts on network proxies. The server will buffer unconsumed data up to a configurable limit; if the client falls too far behind, the connection will be closed. If the `after` version has been truncated from the log, this endpoint will return a standard `410 Gone` HTTP error instead of an event stream.
|
||||
1. **Connection Handling & Errors**: The server may periodically send `keepalive` comments to prevent idle timeouts on network proxies. The server will buffer unconsumed data up to a configurable limit; if the client falls too far behind, the connection will be closed. If the `after` version has been truncated from the log, this endpoint will return a standard `410 Gone` HTTP error instead of an event stream.
|
||||
|
||||
## `PUT /v1/retention/<policy_id>`
|
||||
|
||||
@@ -211,10 +211,10 @@ Creates or updates a retention policy.
|
||||
|
||||
### Response
|
||||
|
||||
* `201 Created` if the policy was created.
|
||||
* `200 OK` if the policy was updated.
|
||||
- `201 Created` if the policy was created.
|
||||
- `200 OK` if the policy was updated.
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /v1/retention/<policy_id>`
|
||||
|
||||
@@ -228,7 +228,7 @@ Retrieves a retention policy by ID.
|
||||
}
|
||||
```
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /v1/retention/`
|
||||
|
||||
@@ -245,7 +245,7 @@ Retrieves all retention policies.
|
||||
]
|
||||
```
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `DELETE /v1/retention/<policy_id>`
|
||||
|
||||
@@ -255,7 +255,7 @@ Removes a retention policy, which may allow the log to be truncated.
|
||||
|
||||
`204 No Content`
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /ok`
|
||||
|
||||
@@ -265,7 +265,7 @@ Simple health check endpoint.
|
||||
|
||||
Returns `200 OK` with minimal content for basic health monitoring.
|
||||
|
||||
-----
|
||||
______________________________________________________________________
|
||||
|
||||
## `GET /metrics`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user