mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-08 22:12:56 +00:00
Support timeline creations on the storage controller to opt out from their creation on the safekeepers, introducing the read-only timelines concept. Read only timelines: * will never receive WAL of their own, so it's fine to not create them on the safekeepers * the property is non-transitive. children of read-only timelines aren't neccessarily read-only themselves. This feature can be used for snapshots, to prevent the safekeepers from being overloaded by empty timelines that won't ever get written to. In the current world, this is not a problem, because timelines are created implicitly by the compute connecting to a safekeeper that doesn't have the timeline yet. In the future however, where the storage controller creates timelines eagerly, we should watch out for that. We represent read-only timelines in the storage controller database so that we ensure that they never touch the safekeepers at all. Especially we don't want them to cause a mess during the importing process of the timelines from the cplane to the storcon database. In a hypothetical future where we have a feature to detach timelines from safekeepers, we'll either need to find a way to distinguish the two, or if not, asking safekeepers to list the (empty) timeline prefix and delete everything from it isn't a big issue either. This patch will unconditionally hit the new safekeeper timeline creation path for read-only timelines, without them needing the `--timelines-onto-safekeepers` flag enabled. This is done because it's lower risk (no safekeepers or computes involved at all) and gives us some initial way to verify at least some parts of that code in prod. https://github.com/neondatabase/cloud/issues/29435 https://github.com/neondatabase/neon/issues/11670
1408 lines
40 KiB
YAML
1408 lines
40 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/reload_auth_validation_keys:
|
|
post:
|
|
description: Reloads the JWT public keys from their pre-configured location on disk.
|
|
responses:
|
|
"200":
|
|
description: The reload completed successfully.
|
|
|
|
/v1/tenant/{tenant_id}:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
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"
|
|
|
|
delete:
|
|
description: |
|
|
Attempts to delete specified tenant. 500, 503 and 409 errors should be retried. Deleting
|
|
a non-existent tenant is considered successful (returns 200).
|
|
responses:
|
|
"200":
|
|
description: Tenant was successfully deleted, or was already not found.
|
|
"503":
|
|
description: Service is unavailable, or tenant is already being modified (perhaps concurrently deleted)
|
|
|
|
|
|
/v1/tenant/{tenant_id}/time_travel_remote_storage:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: travel_to
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: done_if_after
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
put:
|
|
description: Time travel the tenant's remote storage
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: string
|
|
|
|
/v1/tenant/{tenant_id}/timeline:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
get:
|
|
description: Get timelines for tenant
|
|
responses:
|
|
"200":
|
|
description: TimelineInfo
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TimelineInfo"
|
|
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- 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"
|
|
|
|
delete:
|
|
description: "Attempts to delete specified timeline. 500 and 409 errors should be retried"
|
|
responses:
|
|
"404":
|
|
description: Timeline not found. This is the success path.
|
|
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"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}/get_timestamp_of_lsn:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
get:
|
|
description: Get timestamp for a given LSN
|
|
parameters:
|
|
- name: lsn
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
description: A LSN to get the timestamp
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
"412":
|
|
description: No timestamp is found for given LSN, e.g. if there had been no commits till LSN
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PreconditionFailedError"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}/get_lsn_by_timestamp:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- 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
|
|
- name: with_lease
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: boolean
|
|
description: Whether to grant a lease to the corresponding LSN. Default to false.
|
|
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/LsnByTimestampResponse"
|
|
|
|
/v1/tenant/{tenant_shard_id}/timeline/{timeline_id}/lsn_lease:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: Obtains a lease for the given LSN.
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- lsn
|
|
properties:
|
|
lsn:
|
|
description: A LSN to obtain the lease for.
|
|
type: string
|
|
format: hex
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/LsnLease"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}/do_gc:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- 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
|
|
|
|
/v1/tenant/{tenant_shard_id}/timeline/{timeline_id}/block_gc:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: Persistently add a gc blocking at the tenant level because of this timeline
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
|
|
/v1/tenant/{tenant_shard_id}/timeline/{timeline_id}/unblock_gc:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: hex
|
|
post:
|
|
description: Persistently remove a tenant level gc blocking for this timeline
|
|
responses:
|
|
"200":
|
|
description: OK
|
|
|
|
/v1/tenant/{tenant_shard_id}/location_config:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: flush_ms
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
- name: lazy
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: boolean
|
|
description: Set to true for attaches to queue up until activated by compute. Eager (false) is the default.
|
|
put:
|
|
description: |
|
|
Configures a _tenant location_, that is how a particular pageserver handles
|
|
a particular tenant. This includes _attached_ tenants, i.e. those ingesting WAL
|
|
and page service requests, and _secondary_ tenants, i.e. those which are just keeping
|
|
a warm cache in anticipation of transitioning to attached state in the future.
|
|
|
|
This is a declarative, idempotent API: there are not separate endpoints
|
|
for different tenant location configurations. Rather, this single endpoint accepts
|
|
a description of the desired location configuration, and makes whatever changes
|
|
are required to reach that state.
|
|
|
|
In imperative terms, this API is used to attach and detach tenants, and
|
|
to transition tenants to and from secondary mode.
|
|
|
|
This is a synchronous API: there is no 202 response. State transitions should always
|
|
be fast (milliseconds), with the exception of requests setting `flush_ms`, in which case
|
|
the caller controls the runtime of the request.
|
|
|
|
In some state transitions, it makes sense to flush dirty data to remote storage: this includes transitions
|
|
to AttachedStale and Detached. Flushing is never necessary for correctness, but is an
|
|
important optimization when doing migrations. The `flush_ms` parameter controls whether
|
|
flushing should be attempted, and how much time is allowed for flushing. If the time limit expires,
|
|
the requested transition will continue without waiting for any outstanding data to flush. Callers
|
|
should use a duration which is substantially less than their HTTP client's request
|
|
timeout. It is safe to supply flush_ms irrespective of the request body: in state transitions
|
|
where flushing doesn't make sense, the server will ignore it.
|
|
|
|
It is safe to retry requests, but if one receives a 409 or 503 response, it is not
|
|
useful to retry aggressively: there is probably an existing request still ongoing.
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantLocationConfigRequest"
|
|
responses:
|
|
"200":
|
|
description: Tenant is now in requested state
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantLocationConfigResponse"
|
|
"409":
|
|
description: |
|
|
The tenant is already being modified, perhaps by a concurrent call to this API
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/{timeline_id}/preserve_initdb_archive:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
post:
|
|
description: |
|
|
Marks the initdb archive for preservation upon deletion of the timeline or tenant.
|
|
This is meant to be part of the disaster recovery process.
|
|
responses:
|
|
"202":
|
|
description: Tenant scheduled to load successfully
|
|
|
|
/v1/tenant/{tenant_shard_id}/timeline/{timeline_id}/archival_config:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
put:
|
|
description: |
|
|
Either archives or unarchives the given timeline.
|
|
An archived timeline may not have any non-archived children.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ArchivalConfigRequest"
|
|
responses:
|
|
"200":
|
|
description: Timeline (un)archived successfully
|
|
"409":
|
|
description: |
|
|
The tenant/timeline is already being modified, perhaps by a concurrent call to this API
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"500":
|
|
description: Generic operation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"503":
|
|
description: Temporarily unavailable, please retry.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ServiceUnavailableError"
|
|
|
|
/v1/tenant/{tenant_id}/synthetic_size:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- 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:
|
|
$ref: "#/components/schemas/SyntheticSizeResponse"
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
description: SVG representation of the tenant and its timelines.
|
|
"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"
|
|
"503":
|
|
description: Temporarily unavailable, please retry.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ServiceUnavailableError"
|
|
|
|
/v1/tenant/{tenant_shard_id}/heatmap_upload:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
post:
|
|
description: |
|
|
If the location is in an attached mode, upload the current state to the remote heatmap
|
|
responses:
|
|
"200":
|
|
description: Success
|
|
|
|
/v1/tenant/{tenant_shard_id}/secondary/download:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: wait_ms
|
|
description: If set, we will wait this long for download to complete, and if it isn't complete then return 202
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
post:
|
|
description: |
|
|
If the location is in secondary mode, download latest heatmap and layers
|
|
responses:
|
|
"200":
|
|
description: Success
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SecondaryProgress"
|
|
"202":
|
|
description: Download has started but not yet finished
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SecondaryProgress"
|
|
|
|
/v1/tenant/{tenant_id}/timeline/:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
post:
|
|
description: |
|
|
Create a timeline. Returns new timeline id on success.
|
|
Recreating the same timeline will succeed if the parameters match the existing timeline.
|
|
If no pg_version is specified, assume DEFAULT_PG_VERSION hardcoded in the pageserver.
|
|
|
|
To ensure durability, the caller must retry the creation until success.
|
|
Just because the timeline is visible via other endpoints does not mean it is durable.
|
|
Future versions may stop showing timelines that are not yet durable.
|
|
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
|
|
read_only:
|
|
type: boolean
|
|
existing_initdb_timeline_id:
|
|
type: string
|
|
format: hex
|
|
import_pgdata:
|
|
$ref: "#/components/schemas/TimelineCreateRequestImportPgdata"
|
|
responses:
|
|
"201":
|
|
description: Timeline was created, or already existed with matching parameters
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TimelineInfo"
|
|
"406":
|
|
description: Permanently unsatisfiable request, don't retry.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"409":
|
|
description: Timeline already exists, with different parameters. Creation cannot proceed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
"429":
|
|
description: A creation request was sent for the same Timeline Id while a creation was already in progress. Back off and retry.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/v1/tenant/{tenant_shard_id}/timeline/{timeline_id}/detach_ancestor:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
|
|
put:
|
|
description: |
|
|
Detach a timeline from its ancestor and reparent all ancestors timelines with lower `ancestor_lsn`.
|
|
Current implementation might not be retryable across failure cases, but will be enhanced in future.
|
|
Detaching should be expected to be expensive operation. Timeouts should be retried.
|
|
parameters:
|
|
- name: detach_behavior
|
|
in: query
|
|
required: false
|
|
schema:
|
|
description: Currently valid values are `v1`, `v2`
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: |
|
|
The timeline has been detached from it's ancestor (now or earlier), and at least the returned timelines have been reparented.
|
|
If any timelines were deleted after reparenting, they might not be on this list.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/AncestorDetached"
|
|
|
|
"400":
|
|
description: |
|
|
Number of early checks meaning the timeline cannot be detached now:
|
|
- the ancestor of timeline has an ancestor: not supported, see RFC
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
"404":
|
|
description: Tenant or timeline not found.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/NotFoundError"
|
|
|
|
"409":
|
|
description: |
|
|
The timeline can never be detached:
|
|
- timeline has no ancestor, implying that the timeline has never had an ancestor
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
|
|
"500":
|
|
description: |
|
|
Transient error, for example, pageserver shutdown happened while
|
|
processing the request but we were unable to distinguish that. Must
|
|
be retried.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
"503":
|
|
description: |
|
|
Temporarily unavailable, please retry. Possible reasons:
|
|
- another timeline detach for the same tenant is underway, please retry later
|
|
- detected shutdown error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ServiceUnavailableError"
|
|
|
|
|
|
/v1/tenant/:
|
|
get:
|
|
description: Get tenants list
|
|
responses:
|
|
"200":
|
|
description: TenantInfo
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TenantInfo"
|
|
|
|
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
|
|
"409":
|
|
description: Tenant already exists, creation skipped
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ConflictError"
|
|
|
|
/v1/tenant/config:
|
|
put:
|
|
description: |
|
|
Update tenant's config by setting it to the provided value
|
|
|
|
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"
|
|
patch:
|
|
description: |
|
|
Update tenant's config additively by patching the updated fields provided.
|
|
Null values unset the field and non-null values upsert it.
|
|
|
|
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"
|
|
|
|
/v1/tenant/{tenant_id}/config/:
|
|
parameters:
|
|
- name: tenant_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
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"
|
|
|
|
/v1/tenant/{tenant_shard_id}/timeline/{timeline_id}/download_heatmap_layers:
|
|
parameters:
|
|
- name: tenant_shard_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: timeline_id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: concurrency
|
|
description: Maximum number of concurrent downloads (capped at remote storage concurrency)
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
- name: recurse
|
|
description: When set, will recurse with the downloads into ancestor timelines
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: boolean
|
|
post:
|
|
description: |
|
|
Download all layers in the specified timeline's heatmap. The `tenant_shard_id` parameter
|
|
may be used to target all shards of a tenant when the unsharded form is used, or a specific
|
|
tenant shard with the sharded form.
|
|
responses:
|
|
"200":
|
|
description: Success
|
|
delete:
|
|
description: Stop any on-going background downloads of heatmap layers for the specified timeline.
|
|
responses:
|
|
"200":
|
|
description: Success
|
|
|
|
/v1/utilization:
|
|
get:
|
|
description: |
|
|
Returns the pageservers current utilization and fitness score for new tenants.
|
|
|
|
responses:
|
|
"200":
|
|
description: Pageserver utilization and fitness score
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PageserverUtilization"
|
|
|
|
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.
|
|
type: object
|
|
required:
|
|
- slug
|
|
- data
|
|
properties:
|
|
slug:
|
|
type: string
|
|
enum: [ "maybe", "attached", "failed" ]
|
|
data:
|
|
type: object
|
|
properties:
|
|
reason:
|
|
type: string
|
|
gc_blocking:
|
|
type: string
|
|
|
|
TenantCreateRequest:
|
|
allOf:
|
|
- $ref: '#/components/schemas/TenantConfig'
|
|
- $ref: '#/components/schemas/TenantLoadRequest'
|
|
- type: object
|
|
required:
|
|
- new_tenant_id
|
|
properties:
|
|
new_tenant_id:
|
|
type: string
|
|
TenantLoadRequest:
|
|
type: object
|
|
properties:
|
|
generation:
|
|
type: integer
|
|
description: Attachment generation number.
|
|
TenantConfigRequest:
|
|
allOf:
|
|
- $ref: '#/components/schemas/TenantConfig'
|
|
- type: object
|
|
required:
|
|
- tenant_id
|
|
properties:
|
|
tenant_id:
|
|
type: string
|
|
TenantLocationConfigRequest:
|
|
type: object
|
|
required:
|
|
- mode
|
|
properties:
|
|
mode:
|
|
type: string
|
|
enum: ["AttachedSingle", "AttachedMulti", "AttachedStale", "Secondary", "Detached"]
|
|
description: Mode of functionality that this pageserver will run in for this tenant.
|
|
generation:
|
|
type: integer
|
|
description: Attachment generation number, mandatory when `mode` is an attached state
|
|
secondary_conf:
|
|
$ref: '#/components/schemas/SecondaryConfig'
|
|
tenant_conf:
|
|
$ref: '#/components/schemas/TenantConfig'
|
|
TenantLocationConfigResponse:
|
|
type: object
|
|
required:
|
|
- shards
|
|
properties:
|
|
shards:
|
|
description: Pageservers where this tenant's shards are attached. Not populated for secondary locations.
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TenantShardLocation"
|
|
stripe_size:
|
|
description: If multiple shards are present, this field contains the sharding stripe size, else it is null.
|
|
type: integer
|
|
nullable: true
|
|
TenantShardLocation:
|
|
type: object
|
|
required:
|
|
- node_id
|
|
- shard_id
|
|
properties:
|
|
node_id:
|
|
description: Pageserver node ID where this shard is attached
|
|
type: integer
|
|
shard_id:
|
|
description: Tenant shard ID of the shard
|
|
type: string
|
|
SecondaryConfig:
|
|
type: object
|
|
properties:
|
|
warm:
|
|
type: boolean
|
|
description: Whether to poll remote storage for layers to download. If false, secondary locations don't download anything.
|
|
ArchivalConfigRequest:
|
|
type: object
|
|
required:
|
|
- state
|
|
properties:
|
|
state:
|
|
description: The archival state of a timeline
|
|
type: string
|
|
enum: ["Archived", "Unarchived"]
|
|
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
|
|
compaction_upper_limit:
|
|
type: string
|
|
image_creation_threshold:
|
|
type: integer
|
|
walreceiver_connect_timeout:
|
|
type: string
|
|
lagging_wal_timeout:
|
|
type: string
|
|
max_lsn_wal_lag:
|
|
type: integer
|
|
heatmap_period:
|
|
type: string
|
|
TenantConfigResponse:
|
|
type: object
|
|
properties:
|
|
tenant_specific_overrides:
|
|
$ref: "#/components/schemas/TenantConfig"
|
|
effective_config:
|
|
$ref: "#/components/schemas/TenantConfig"
|
|
TimelineCreateRequestImportPgdata:
|
|
type: object
|
|
required:
|
|
- location
|
|
- idempotency_key
|
|
properties:
|
|
idempotency_key:
|
|
type: string
|
|
location:
|
|
$ref: "#/components/schemas/TimelineCreateRequestImportPgdataLocation"
|
|
TimelineCreateRequestImportPgdataLocation:
|
|
type: object
|
|
properties:
|
|
AwsS3:
|
|
$ref: "#/components/schemas/TimelineCreateRequestImportPgdataLocationAwsS3"
|
|
TimelineCreateRequestImportPgdataLocationAwsS3:
|
|
type: object
|
|
properties:
|
|
region:
|
|
type: string
|
|
bucket:
|
|
type: string
|
|
key:
|
|
type: string
|
|
required:
|
|
- region
|
|
- bucket
|
|
- key
|
|
TimelineInfo:
|
|
type: object
|
|
required:
|
|
- timeline_id
|
|
- tenant_id
|
|
- last_record_lsn
|
|
- disk_consistent_lsn
|
|
- state
|
|
- min_readable_lsn
|
|
properties:
|
|
timeline_id:
|
|
type: string
|
|
format: hex
|
|
tenant_id:
|
|
type: string
|
|
last_record_lsn:
|
|
type: string
|
|
format: hex
|
|
disk_consistent_lsn:
|
|
type: string
|
|
format: hex
|
|
remote_consistent_lsn:
|
|
type: string
|
|
format: hex
|
|
remote_consistent_lsn_visible:
|
|
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
|
|
min_readable_lsn:
|
|
type: string
|
|
format: hex
|
|
applied_gc_cutoff_lsn:
|
|
type: string
|
|
format: hex
|
|
safekeepers:
|
|
$ref: "#/components/schemas/TimelineSafekeepersInfo"
|
|
|
|
TimelineSafekeepersInfo:
|
|
type: object
|
|
required:
|
|
- tenant_id
|
|
- timeline_id
|
|
- generation
|
|
- safekeepers
|
|
properties:
|
|
tenant_id:
|
|
type: string
|
|
format: hex
|
|
timeline_id:
|
|
type: string
|
|
format: hex
|
|
generation:
|
|
type: integer
|
|
safekeepers:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TimelineSafekeeperInfo"
|
|
|
|
TimelineSafekeeperInfo:
|
|
type: object
|
|
required:
|
|
- id
|
|
- hostname
|
|
properties:
|
|
id:
|
|
type: integer
|
|
hostname:
|
|
type: string
|
|
|
|
SyntheticSizeResponse:
|
|
type: object
|
|
required:
|
|
- id
|
|
- size
|
|
- segment_sizes
|
|
- inputs
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: hex
|
|
size:
|
|
type: integer
|
|
nullable: true
|
|
description: |
|
|
Size metric in bytes or null if inputs_only=true was given.
|
|
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
|
|
|
|
LsnByTimestampResponse:
|
|
type: object
|
|
required:
|
|
- lsn
|
|
- kind
|
|
properties:
|
|
lsn:
|
|
type: string
|
|
format: hex
|
|
kind:
|
|
type: string
|
|
enum: [past, present, future, nodata]
|
|
valid_until:
|
|
type: string
|
|
format: date-time
|
|
description: The expiration time of the granted lease.
|
|
|
|
LsnLease:
|
|
type: object
|
|
required:
|
|
- valid_until
|
|
properties:
|
|
valid_until:
|
|
type: string
|
|
format: date-time
|
|
|
|
PageserverUtilization:
|
|
type: object
|
|
required:
|
|
- disk_usage_bytes
|
|
- free_space_bytes
|
|
- utilization_score
|
|
properties:
|
|
disk_usage_bytes:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: The amount of disk space currently used.
|
|
free_space_bytes:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: The amount of usable disk space left.
|
|
utilization_score:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
maximum: 9223372036854775807
|
|
default: 9223372036854775807
|
|
description: |
|
|
Lower is better score for how good this pageserver would be for the next tenant.
|
|
The default or maximum value can be returned in situations when a proper score cannot (yet) be calculated.
|
|
|
|
SecondaryProgress:
|
|
type: object
|
|
required:
|
|
- heatmap_mtime
|
|
- layers_downloaded
|
|
- layers_total
|
|
- bytes_downloaded
|
|
- bytes_total
|
|
properties:
|
|
heatmap_mtime:
|
|
type: string
|
|
format: date-time
|
|
description: Modification time of the most recently downloaded layer heatmap (RFC 3339 format)
|
|
layers_downloaded:
|
|
type: integer
|
|
format: int64
|
|
description: How many layers from the latest layer heatmap are present on disk
|
|
bytes_downloaded:
|
|
type: integer
|
|
format: int64
|
|
description: How many bytes of layer content from the latest layer heatmap are present on disk
|
|
layers_total:
|
|
type: integer
|
|
format: int64
|
|
description: How many layers were in the latest layer heatmap
|
|
bytes_total:
|
|
type: integer
|
|
format: int64
|
|
description: How many bytes of layer content were in the latest layer heatmap
|
|
|
|
AncestorDetached:
|
|
type: object
|
|
required:
|
|
- reparented_timelines
|
|
properties:
|
|
reparented_timelines:
|
|
type: array
|
|
description: Set of reparented timeline ids
|
|
items:
|
|
type: string
|
|
format: hex
|
|
description: TimelineId
|
|
|
|
|
|
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
|
|
ServiceUnavailableError:
|
|
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: []
|