mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-17 05:00:38 +00:00
## Problem - #5050 Closes: https://github.com/neondatabase/neon/issues/5136 ## Summary of changes - A new configuration property `control_plane_api` controls other functionality in this PR: if it is unset (default) then everything still works as it does today. - If `control_plane_api` is set, then on startup we call out to control plane `/re-attach` endpoint to discover our attachments and their generations. If an attachment is missing from the response we implicitly detach the tenant. - Calls to pageserver `/attach` API may include a `generation` parameter. If `control_plane_api` is set, then this parameter is mandatory. - RemoteTimelineClient's loading of index_part.json is generation-aware, and will try to load the index_part with the most recent generation <= its own generation. - The `neon_local` testing environment now includes a new binary `attachment_service` which implements the endpoints that the pageserver requires to operate. This is on by default if running `cargo neon` by hand. In `test_runner/` tests, it is off by default: existing tests continue to run with in the legacy generation-less mode. Caveats: - The re-attachment during startup assumes that we are only re-attaching tenants that have previously been attached, and not totally new tenants -- this relies on the control plane's attachment logic to keep retrying so that we should eventually see the attach API call. That's important because the `/re-attach` API doesn't tell us which timelines we should attach -- we still use local disk state for that. Ref: https://github.com/neondatabase/neon/issues/5173 - Testing: generations are only enabled for one integration test right now (test_pageserver_restart), as a smoke test that all the machinery basically works. Writing fuller tests that stress tenant migration will come later, and involve extending our test fixtures to deal with multiple pageservers. - I'm not in love with "attachment_service" as a name for the neon_local component, but it's not very important because we can easily rename these test bits whenever we want. - Limited observability when in re-attach on startup: when I add generation validation for deletions in a later PR, I want to wrap up the control plane API calls in some small client class that will expose metrics for things like errors calling the control plane API, which will act as a strong red signal that something is not right. Co-authored-by: Christian Schwarz <christian@neon.tech> Co-authored-by: Joonas Koivunen <joonas@neon.tech>
1244 lines
36 KiB
YAML
1244 lines
36 KiB
YAML
openapi: "3.0.2"
|
|
info:
|
|
title: Page Server API
|
|
description: Neon Pageserver API
|
|
version: "1.0"
|
|
license:
|
|
name: "Apache"
|
|
url: https://github.com/neondatabase/neon/blob/main/LICENSE
|
|
servers:
|
|
- url: ""
|
|
paths:
|
|
/v1/status:
|
|
description: Healthcheck endpoint
|
|
get:
|
|
description: Healthcheck
|
|
security: []
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- id
|
|
properties:
|
|
id:
|
|
type: integer
|
|
|
|
/v1/disk_usage_eviction/run:
|
|
put:
|
|
description: Do an iteration of disk-usage-based eviction to evict a given amount of disk space.
|
|
security: []
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- evict_bytes
|
|
properties:
|
|
evict_bytes:
|
|
type: integer
|
|
responses:
|
|
"200":
|
|
description: |
|
|
The run completed.
|
|
This does not necessarily mean that we actually evicted `evict_bytes`.
|
|
Examine the returned object for detail, or, just watch the actual effect of the call using `du` or `df`.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
|
|
/v1/tenant/{tenant_id}:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: Get tenant status
|
|
responses:
|
|
"200":
|
|
description: Currently returns the flag whether the tenant has inprogress timeline downloads
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantInfo"
|
|
"400":
|
|
description: Error when no tenant id found in path or no timeline id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
delete:
|
|
description: |
|
|
Attempts to delete specified tenant. 500 and 409 errors should be retried until 404 is retrieved.
|
|
404 means that deletion successfully finished"
|
|
responses:
|
|
"400":
|
|
description: Error when no tenant id found in path
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"404":
|
|
description: Tenant not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/NotFoundError"
|
|
"409":
|
|
description: Deletion is already in progress, continue polling
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/timeline:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: Get timelines for tenant
|
|
responses:
|
|
"200":
|
|
description: TimelineInfo
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TimelineInfo"
|
|
"400":
|
|
description: Error when no tenant id found in path
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: Get info about the timeline
|
|
responses:
|
|
"200":
|
|
description: TimelineInfo
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TimelineInfo"
|
|
"400":
|
|
description: Error when no tenant id found in path or no timeline id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
delete:
|
|
description: "Attempts to delete specified timeline. 500 and 409 errors should be retried"
|
|
responses:
|
|
"400":
|
|
description: Error when no tenant id found in path or no timeline id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"404":
|
|
description: Timeline not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/NotFoundError"
|
|
"409":
|
|
description: Deletion is already in progress, continue polling
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"412":
|
|
description: Tenant is missing, or timeline has children
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PreconditionFailedError"
|
|
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}/get_lsn_by_timestamp:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: Get LSN by a timestamp
|
|
parameters:
|
|
- name: timestamp
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
description: A timestamp to get the LSN
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: string
|
|
"400":
|
|
description: Error when no tenant id found in path, no timeline id or invalid timestamp
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}/do_gc:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
put:
|
|
description: Garbage collect given timeline
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: string
|
|
"400":
|
|
description: Error when no tenant id found in path, no timeline id or invalid timestamp
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
/v1/tenant/{tenant_id}/attach:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: |
|
|
Schedules attach operation to happen in the background for the given tenant.
|
|
As soon as the caller sends this request, it must assume the pageserver
|
|
starts writing to the tenant's S3 state unless it receives one of the
|
|
distinguished errors below that state otherwise.
|
|
|
|
If a client receives a not-distinguished response, e.g., a network timeout,
|
|
it MUST retry the /attach request and poll again for the tenant's
|
|
attachment status.
|
|
|
|
After the client has received a 202, it MUST poll the tenant's
|
|
attachment status (field `attachment_status`) to reach state `attached`.
|
|
If the `attachment_status` is missing, the client MUST retry the `/attach`
|
|
request (goto previous paragraph). This is a robustness measure in case the tenant
|
|
status endpoint is buggy, but the attach operation is ongoing.
|
|
|
|
There is no way to cancel an in-flight request.
|
|
|
|
In any case, the client
|
|
* MUST NOT ASSUME that the /attach request has been lost in the network,
|
|
* MUST NOT ASSUME that the request has been lost, based on the observation
|
|
that a subsequent tenant status request returns 404. The request may
|
|
still be in flight. It must be retried.
|
|
|
|
The client SHOULD supply a `TenantConfig` for the tenant in the request body.
|
|
Settings specified in the config override the pageserver's defaults.
|
|
It is guaranteed that the config settings are applied before the pageserver
|
|
starts operating on the tenant. E.g., if the config specifies a specific
|
|
PITR interval for a tenant, then that setting will be in effect before the
|
|
pageserver starts the garbage collection loop. This enables a client to
|
|
guarantee a specific PITR setting across detach/attach cycles.
|
|
The pageserver will reject the request if it cannot parse the config, or
|
|
if there are any unknown fields in it.
|
|
|
|
If the client does not supply a config, the pageserver will use its defaults.
|
|
This behavior is deprecated: https://github.com/neondatabase/neon/issues/4282
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantAttachRequest"
|
|
responses:
|
|
"202":
|
|
description: Tenant attaching scheduled
|
|
"400":
|
|
description: Bad Request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"404":
|
|
description: Timeline not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/NotFoundError"
|
|
"409":
|
|
description: Tenant download is already in progress
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/detach:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
- name: detach_ignored
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: boolean
|
|
description: |
|
|
When true, allow to detach a tenant which state is ignored.
|
|
post:
|
|
description: |
|
|
Remove tenant data (including all corresponding timelines) from pageserver's memory and file system.
|
|
Files on the remote storage are not affected.
|
|
responses:
|
|
"200":
|
|
description: Tenant detached
|
|
"400":
|
|
description: Error when no tenant id found in path parameters
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"404":
|
|
description: Tenant not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/NotFoundError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/ignore:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: |
|
|
Remove tenant data (including all corresponding timelines) from pageserver's memory.
|
|
Files on local disk and remote storage are not affected.
|
|
|
|
Future pageserver restarts won't load the data back until `load` is called on such tenant.
|
|
responses:
|
|
"200":
|
|
description: Tenant ignored
|
|
"400":
|
|
description: Error when no tenant id found in path parameters
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/load:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: |
|
|
Schedules an operation that attempts to load a tenant from the local disk and
|
|
synchronise it with the remote storage (if enabled), repeating pageserver's restart logic for tenant load.
|
|
If the tenant was ignored before, removes the ignore mark and continues with load scheduling.
|
|
|
|
Errors if the tenant is absent on disk, already present in memory or fails to schedule its load.
|
|
Scheduling a load does not mean that the tenant would load successfully, check tenant status to ensure load correctness.
|
|
responses:
|
|
"202":
|
|
description: Tenant scheduled to load successfully
|
|
"400":
|
|
description: Error when no tenant id found in path parameters
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/synthetic_size:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: |
|
|
Calculate tenant's synthetic size
|
|
responses:
|
|
"200":
|
|
description: Tenant's synthetic size
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SyntheticSizeResponse"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/size:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
- name: inputs_only
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: boolean
|
|
description: |
|
|
When true, skip calculation and only provide the model inputs (for debugging). Defaults to false.
|
|
- name: retention_period
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
description: |
|
|
Override the default retention period (in bytes) used for size calculation.
|
|
get:
|
|
description: |
|
|
Calculate tenant's size, which is a mixture of WAL (bytes) and logical_size (bytes).
|
|
responses:
|
|
"200":
|
|
description: OK,
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- id
|
|
- size
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: hex
|
|
size:
|
|
type: integer
|
|
nullable: true
|
|
description: |
|
|
Size metric in bytes or null if inputs_only=true was given.
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: |
|
|
Create a timeline. Returns new timeline id on success.\
|
|
If no new timeline id is specified in parameters, it would be generated. It's an error to recreate the same timeline.
|
|
If no pg_version is specified, assume DEFAULT_PG_VERSION hardcoded in the pageserver.
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- new_timeline_id
|
|
properties:
|
|
new_timeline_id:
|
|
type: string
|
|
format: hex
|
|
ancestor_timeline_id:
|
|
type: string
|
|
format: hex
|
|
ancestor_start_lsn:
|
|
type: string
|
|
format: hex
|
|
pg_version:
|
|
type: integer
|
|
responses:
|
|
"201":
|
|
description: TimelineInfo
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TimelineInfo"
|
|
"400":
|
|
description: Malformed timeline create request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"406":
|
|
description: Permanently unsatisfiable request, don't retry.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"409":
|
|
description: Timeline already exists, creation skipped
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
/v1/tenant/:
|
|
get:
|
|
description: Get tenants list
|
|
responses:
|
|
"200":
|
|
description: TenantInfo
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TenantInfo"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
post:
|
|
description: |
|
|
Create a tenant. Returns new tenant id on success.
|
|
|
|
If no new tenant id is specified in parameters, it would be generated. It's an error to recreate the same tenant.
|
|
|
|
Invalid fields in the tenant config will cause the request to be rejected with status 400.
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantCreateRequest"
|
|
responses:
|
|
"201":
|
|
description: New tenant created successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
"400":
|
|
description: Malformed tenant create request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"409":
|
|
description: Tenant already exists, creation skipped
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/config:
|
|
put:
|
|
description: |
|
|
Update tenant's config.
|
|
|
|
Invalid fields in the tenant config will cause the request to be rejected with status 400.
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantConfigRequest"
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TenantInfo"
|
|
"400":
|
|
description: Malformed tenant config request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
/v1/tenant/{tenant_id}/config/:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: |
|
|
Returns tenant's config description: specific config overrides a tenant has
|
|
and the effective config.
|
|
responses:
|
|
"200":
|
|
description: Tenant config, specific and effective
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantConfigResponse"
|
|
"400":
|
|
description: Malformed get tenanant config request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"401":
|
|
description: Unauthorized Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UnauthorizedError"
|
|
"403":
|
|
description: Forbidden Error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ForbiddenError"
|
|
"404":
|
|
description: Tenand or timeline were not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/NotFoundError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
components:
|
|
securitySchemes:
|
|
JWT:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
schemas:
|
|
TenantInfo:
|
|
type: object
|
|
required:
|
|
- id
|
|
- attachment_status
|
|
properties:
|
|
id:
|
|
type: string
|
|
current_physical_size:
|
|
type: integer
|
|
attachment_status:
|
|
description: |
|
|
Status of this tenant's attachment to this pageserver.
|
|
|
|
- `maybe` means almost nothing, don't read anything into it
|
|
except for the fact that the pageserver _might_ be already
|
|
writing to the tenant's S3 state, so, DO NOT ATTACH the
|
|
tenant to any other pageserver, or we risk split-brain.
|
|
- `attached` means that the attach operation has completed,
|
|
successfully
|
|
- `failed` means that attach has failed. For reason check corresponding `reason` failed.
|
|
`failed` is the terminal state, retrying attach call wont resolve the issue.
|
|
For example this can be caused by s3 being unreachable. The retry may be implemented
|
|
with call to detach, though it would be better to not automate it and inspec failed state
|
|
manually before proceeding with a retry.
|
|
|
|
See the tenant `/attach` endpoint for more information.
|
|
type: object
|
|
required:
|
|
- slug
|
|
- data
|
|
properties:
|
|
slug:
|
|
type: string
|
|
enum: [ "maybe", "attached", "failed" ]
|
|
data:
|
|
type: object
|
|
properties:
|
|
reason:
|
|
type: string
|
|
|
|
TenantCreateRequest:
|
|
allOf:
|
|
- $ref: '#/components/schemas/TenantConfig'
|
|
- type: object
|
|
required:
|
|
- new_tenant_id
|
|
properties:
|
|
new_tenant_id:
|
|
type: string
|
|
format: hex
|
|
TenantAttachRequest:
|
|
type: object
|
|
required:
|
|
- config
|
|
properties:
|
|
config:
|
|
$ref: '#/components/schemas/TenantConfig'
|
|
generation:
|
|
type: integer
|
|
description: Attachment generation number.
|
|
TenantConfigRequest:
|
|
allOf:
|
|
- $ref: '#/components/schemas/TenantConfig'
|
|
- type: object
|
|
required:
|
|
- tenant_id
|
|
properties:
|
|
tenant_id:
|
|
type: string
|
|
format: hex
|
|
TenantConfig:
|
|
type: object
|
|
properties:
|
|
gc_period:
|
|
type: string
|
|
gc_horizon:
|
|
type: integer
|
|
pitr_interval:
|
|
type: string
|
|
checkpoint_distance:
|
|
type: integer
|
|
checkpoint_timeout:
|
|
type: string
|
|
compaction_target_size:
|
|
type: integer
|
|
compaction_period:
|
|
type: string
|
|
compaction_threshold:
|
|
type: string
|
|
image_creation_threshold:
|
|
type: integer
|
|
walreceiver_connect_timeout:
|
|
type: string
|
|
lagging_wal_timeout:
|
|
type: string
|
|
max_lsn_wal_lag:
|
|
type: integer
|
|
trace_read_requests:
|
|
type: boolean
|
|
TenantConfigResponse:
|
|
type: object
|
|
properties:
|
|
tenant_specific_overrides:
|
|
$ref: "#/components/schemas/TenantConfig"
|
|
effective_config:
|
|
$ref: "#/components/schemas/TenantConfig"
|
|
TimelineInfo:
|
|
type: object
|
|
required:
|
|
- timeline_id
|
|
- tenant_id
|
|
- last_record_lsn
|
|
- disk_consistent_lsn
|
|
- state
|
|
- latest_gc_cutoff_lsn
|
|
properties:
|
|
timeline_id:
|
|
type: string
|
|
format: hex
|
|
tenant_id:
|
|
type: string
|
|
format: hex
|
|
last_record_lsn:
|
|
type: string
|
|
format: hex
|
|
disk_consistent_lsn:
|
|
type: string
|
|
format: hex
|
|
remote_consistent_lsn:
|
|
type: string
|
|
format: hex
|
|
ancestor_timeline_id:
|
|
type: string
|
|
format: hex
|
|
ancestor_lsn:
|
|
type: string
|
|
format: hex
|
|
prev_record_lsn:
|
|
type: string
|
|
format: hex
|
|
current_logical_size:
|
|
type: integer
|
|
current_physical_size:
|
|
type: integer
|
|
wal_source_connstr:
|
|
type: string
|
|
last_received_msg_lsn:
|
|
type: string
|
|
format: hex
|
|
last_received_msg_ts:
|
|
type: integer
|
|
state:
|
|
type: string
|
|
latest_gc_cutoff_lsn:
|
|
type: string
|
|
format: hex
|
|
|
|
SyntheticSizeResponse:
|
|
type: object
|
|
required:
|
|
- id
|
|
- size
|
|
- segment_sizes
|
|
- inputs
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: hex
|
|
size:
|
|
type: integer
|
|
segment_sizes:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/SegmentSize"
|
|
inputs:
|
|
type: object
|
|
properties:
|
|
segments:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/SegmentData"
|
|
timeline_inputs:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TimelineInput"
|
|
|
|
SegmentSize:
|
|
type: object
|
|
required:
|
|
- method
|
|
- accum_size
|
|
properties:
|
|
method:
|
|
type: string
|
|
accum_size:
|
|
type: integer
|
|
|
|
SegmentData:
|
|
type: object
|
|
required:
|
|
- segment
|
|
properties:
|
|
segment:
|
|
type: object
|
|
required:
|
|
- lsn
|
|
properties:
|
|
parent:
|
|
type: integer
|
|
lsn:
|
|
type: integer
|
|
size:
|
|
type: integer
|
|
needed:
|
|
type: boolean
|
|
timeline_id:
|
|
type: string
|
|
format: hex
|
|
kind:
|
|
type: string
|
|
|
|
TimelineInput:
|
|
type: object
|
|
required:
|
|
- timeline_id
|
|
properties:
|
|
ancestor_id:
|
|
type: string
|
|
ancestor_lsn:
|
|
type: string
|
|
timeline_id:
|
|
type: string
|
|
format: hex
|
|
|
|
Error:
|
|
type: object
|
|
required:
|
|
- msg
|
|
properties:
|
|
msg:
|
|
type: string
|
|
UnauthorizedError:
|
|
type: object
|
|
required:
|
|
- msg
|
|
properties:
|
|
msg:
|
|
type: string
|
|
ForbiddenError:
|
|
type: object
|
|
required:
|
|
- msg
|
|
properties:
|
|
msg:
|
|
type: string
|
|
NotFoundError:
|
|
type: object
|
|
required:
|
|
- msg
|
|
properties:
|
|
msg:
|
|
type: string
|
|
ConflictError:
|
|
type: object
|
|
required:
|
|
- msg
|
|
properties:
|
|
msg:
|
|
type: string
|
|
PreconditionFailedError:
|
|
type: object
|
|
required:
|
|
- msg
|
|
properties:
|
|
msg:
|
|
type: string
|
|
|
|
security:
|
|
- JWT: []
|