Skip to content

Commit 139cfe6

Browse files
committed
[nexus] start DB model for webhooks
1 parent dcc0df3 commit 139cfe6

File tree

13 files changed

+277
-0
lines changed

13 files changed

+277
-0
lines changed

schema/crdb/add-webhooks/README.adoc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Overview
2+
3+
This migration adds initial tables required for webhook delivery.
4+
5+
## Upgrade steps
6+
7+
The individual transactions in this upgrade do the following:
8+
9+
* *Webhook receivers*:
10+
** `up01.sql` creates the `omicron.public.webhook_rx` table, which stores
11+
the receiver endpoints that receive webhook events.
12+
** *Receiver secrets*:
13+
*** `up02.sql` creates the `omicron.public.webhook_rx_secret` table, which
14+
associates webhook receivers with secret keys and their IDs.
15+
*** `up03.sql` creates the `lookup_webhook_secrets_by_rx` index on that table,
16+
for looking up all secrets associated with a receiver.
17+
** *Receiver subscriptions*:
18+
*** `up04.sql` creates the `omicron.public.webhook_rx_subscription` table, which
19+
associates a webhook receiver with multiple event classes that the receiver is
20+
subscribed to.
21+
*** `up05.sql` creates an index `lookup_webhook_subscriptions_by_rx` for
22+
looking up all event classes that a receiver ID is subscribed to.
23+
* *Webhook message dispatching and delivery attempts*:
24+
** *Dispatch table*:
25+
*** `up06.sql` creates the table `omicron.public.webhook_msg_dispatch`, which
26+
tracks the webhook messages that have been dispatched to receivers.
27+
*** `up07.sql` creates an index `lookup_webhook_dispatched_to_rx` for looking up
28+
entries in `omicron.public.webhook_msg_dispatch` by receiver ID.
29+
*** `up08.sql` creates an index `webhook_dispatch_in_flight` for looking up all currently in-flight webhook
30+
messages (entries in `omicron.public.webhook_msg_dispatch` where the
31+
`time_completed` field has not been set).
32+
** *Delivery attempts*:
33+
*** `up09.sql` creates the enum `omicron.public.webhook_msg_delivery_result`,
34+
representing the potential outcomes of a webhook delivery attempt.
35+
*** `up10.sql` creates the table `omicron.public.webhook_msg_delivery_attempt`,
36+
which records each individual delivery attempt for a webhook message in the
37+
`webhook_msg_dispatch` table.
38+
*** `up11.sql` creates an index `lookup_webhook_delivery_attempts_for_msg` on
39+
`omicron.public.webhook_msg_delivery_attempt`, for looking up all attempts to
40+
deliver a message with a given dispatch ID.

schema/crdb/add-webhooks/up01.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_rx (
2+
id UUID PRIMARY KEY,
3+
-- A human-readable identifier for this webhook receiver.
4+
name STRING(63) NOT NULL,
5+
-- URL of the endpoint webhooks are delivered to.
6+
endpoint STRING(512) NOT NULL,
7+
-- TODO(eliza): how do we track which roles are assigned to a webhook?
8+
time_created TIMESTAMPTZ NOT NULL,
9+
time_deleted TIMESTAMPTZ
10+
);

schema/crdb/add-webhooks/up02.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_rx_secret (
2+
-- UUID of the webhook receiver (foreign key into
3+
-- `omicron.public.webhook_rx`)
4+
rx_id UUID NOT NULL,
5+
-- ID of this secret.
6+
signature_id STRING(63) NOT NULL,
7+
-- Secret value.
8+
secret BYTES NOT NULL,
9+
time_created TIMESTAMPTZ NOT NULL,
10+
time_deleted TIMESTAMPTZ,
11+
12+
PRIMARY KEY (signature_id, rx_id)
13+
);

schema/crdb/add-webhooks/up03.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE INDEX IF NOT EXISTS lookup_webhook_secrets_by_rx
2+
ON omicron.public.webhook_rx_secret (
3+
rx_id
4+
) WHERE
5+
time_deleted IS NULL;

schema/crdb/add-webhooks/up04.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_rx_subscription (
2+
-- UUID of the webhook receiver (foreign key into
3+
-- `omicron.public.webhook_rx`)
4+
rx_id UUID NOT NULL,
5+
-- An event class to which this receiver is subscribed.
6+
event_class STRING(512) NOT NULL,
7+
time_created TIMESTAMPTZ NOT NULL,
8+
9+
PRIMARY KEY (rx_id, event_class)
10+
);

schema/crdb/add-webhooks/up05.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CREATE INDEX IF NOT EXISTS lookup_webhook_subscriptions_by_rx
2+
ON omicron.public.webhook_rx_subscription (
3+
rx_id
4+
);

schema/crdb/add-webhooks/up06.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_msg_dispatch (
2+
-- UUID of this dispatch.
3+
id UUID PRIMARY KEY,
4+
-- UUID of the webhook receiver (foreign key into
5+
-- `omicron.public.webhook_rx`)
6+
rx_id UUID NOT NULL,
7+
payload JSONB NOT NULL,
8+
time_created TIMESTAMPTZ NOT NULL,
9+
-- If this is set, then this webhook message has either been delivered
10+
-- successfully, or is considered permanently failed.
11+
time_completed TIMESTAMPTZ,
12+
);

schema/crdb/add-webhooks/up07.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Index for looking up all webhook messages dispatched to a receiver ID
2+
CREATE INDEX IF NOT EXISTS lookup_webhook_dispatched_to_rx
3+
ON omicron.public.webhook_msg_dispatch (
4+
rx_id
5+
);

schema/crdb/add-webhooks/up08.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Index for looking up all currently in-flight webhook messages, and ordering
2+
-- them by their creation times.
3+
CREATE INDEX IF NOT EXISTS webhook_dispatch_in_flight
4+
ON omicron.public.webhook_msg_dispatch (
5+
time_created, id
6+
) WHERE
7+
time_completed IS NULL;

schema/crdb/add-webhooks/up09.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CREATE TYPE IF NOT EXISTS omicron.public.webhook_msg_delivery_result as ENUM (
2+
-- The delivery attempt failed with an HTTP error.
3+
'failed_http_error',
4+
-- The delivery attempt failed because the receiver endpoint was
5+
-- unreachable.
6+
'failed_unreachable',
7+
-- The delivery attempt succeeded.
8+
'succeeded'
9+
);

schema/crdb/add-webhooks/up10.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_msg_delivery_attempt (
2+
id UUID PRIMARY KEY,
3+
-- Foreign key into `omicron.public.webhook_msg_dispatch`.
4+
dispatch_id UUID NOT NULL,
5+
result omicron.public.webhook_msg_delivery_result NOT NULL,
6+
response_status INT2,
7+
response_duration INTERVAL,
8+
time_created TIMESTAMPTZ NOT NULL,
9+
10+
CONSTRAINT response_iff_not_unreachable CHECK (
11+
(
12+
-- If the result is 'succeedeed' or 'failed_http_error', response
13+
-- data must be present.
14+
(result = 'succeeded' OR result = 'failed_http_error') AND (
15+
response_status IS NOT NULL AND
16+
response_duration IS NOT NULL
17+
)
18+
) OR (
19+
-- If the result is 'failed_unreachable', no response data is
20+
-- present.
21+
(result = 'failed_unreachable') AND (
22+
response_status IS NULL AND
23+
response_duration IS NULL
24+
)
25+
)
26+
)
27+
);

schema/crdb/add-webhooks/up11.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CREATE INDEX IF NOT EXISTS lookup_webhook_delivery_attempts_for_msg
2+
ON omicron.public.webhook_msg_delivery_attempts (
3+
dispatch_id
4+
);

schema/crdb/dbinit.sql

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4683,6 +4683,137 @@ CREATE UNIQUE INDEX IF NOT EXISTS one_record_per_volume_resource_usage on omicro
46834683
region_snapshot_snapshot_id
46844684
);
46854685

4686+
/*
4687+
* WEBHOOKS
4688+
*/
4689+
4690+
4691+
/*
4692+
* Webhook receivers, receiver secrets, and receiver subscriptions.
4693+
*/
4694+
4695+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_rx (
4696+
id UUID PRIMARY KEY,
4697+
-- A human-readable identifier for this webhook receiver.
4698+
name STRING(63) NOT NULL,
4699+
-- URL of the endpoint webhooks are delivered to.
4700+
endpoint STRING(512) NOT NULL,
4701+
-- TODO(eliza): how do we track which roles are assigned to a webhook?
4702+
time_created TIMESTAMPTZ NOT NULL,
4703+
time_modified TIMESTAMPTZ,
4704+
time_deleted TIMESTAMPTZ
4705+
);
4706+
4707+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_rx_secret (
4708+
-- UUID of the webhook receiver (foreign key into
4709+
-- `omicron.public.webhook_rx`)
4710+
rx_id UUID NOT NULL,
4711+
-- ID of this secret.
4712+
signature_id STRING(63) NOT NULL,
4713+
-- Secret value.
4714+
secret BYTES NOT NULL,
4715+
time_created TIMESTAMPTZ NOT NULL,
4716+
time_deleted TIMESTAMPTZ,
4717+
4718+
PRIMARY KEY (signature_id, rx_id)
4719+
);
4720+
4721+
CREATE INDEX IF NOT EXISTS lookup_webhook_secrets_by_rx
4722+
ON omicron.public.webhook_rx_secret (
4723+
rx_id
4724+
) WHERE
4725+
time_deleted IS NULL;
4726+
4727+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_subscription (
4728+
-- UUID of the webhook receiver (foreign key into
4729+
-- `omicron.public.webhook_rx`)
4730+
rx_id UUID NOT NULL,
4731+
-- An event class to which this receiver is subscribed.
4732+
event_class STRING(512) NOT NULL,
4733+
time_created TIMESTAMPTZ NOT NULL,
4734+
4735+
PRIMARY KEY (rx_id, event_class)
4736+
);
4737+
4738+
CREATE INDEX IF NOT EXISTS lookup_webhook_subscriptions_by_rx
4739+
ON omicron.public.webhook_rx_subscription (
4740+
rx_id
4741+
);
4742+
4743+
/*
4744+
* Webhook message dispatching and delivery attempts.
4745+
*/
4746+
4747+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_msg_dispatch (
4748+
-- UUID of this dispatch.
4749+
id UUID PRIMARY KEY,
4750+
-- UUID of the webhook receiver (foreign key into
4751+
-- `omicron.public.webhook_rx`)
4752+
rx_id UUID NOT NULL,
4753+
payload JSONB NOT NULL,
4754+
time_created TIMESTAMPTZ NOT NULL,
4755+
-- If this is set, then this webhook message has either been delivered
4756+
-- successfully, or is considered permanently failed.
4757+
time_completed TIMESTAMPTZ,
4758+
);
4759+
4760+
-- Index for looking up all webhook messages dispatched to a receiver ID
4761+
CREATE INDEX IF NOT EXISTS lookup_webhook_dispatched_to_rx
4762+
ON omicron.public.webhook_msg_dispatch (
4763+
rx_id
4764+
);
4765+
4766+
-- Index for looking up all currently in-flight webhook messages, and ordering
4767+
-- them by their creation times.
4768+
CREATE INDEX IF NOT EXISTS webhook_dispatch_in_flight
4769+
ON omicron.public.webhook_msg_dispatch (
4770+
time_created, id
4771+
) WHERE
4772+
time_completed IS NULL;
4773+
4774+
CREATE TYPE IF NOT EXISTS omicron.public.webhook_msg_delivery_result as ENUM (
4775+
-- The delivery attempt failed with an HTTP error.
4776+
'failed_http_error',
4777+
-- The delivery attempt failed because the receiver endpoint was
4778+
-- unreachable.
4779+
'failed_unreachable',
4780+
-- The delivery attempt succeeded.
4781+
'succeeded'
4782+
);
4783+
4784+
CREATE TABLE IF NOT EXISTS omicron.public.webhook_msg_delivery_attempt (
4785+
id UUID PRIMARY KEY,
4786+
-- Foreign key into `omicron.public.webhook_msg_dispatch`.
4787+
dispatch_id UUID NOT NULL,
4788+
result omicron.public.webhook_msg_delivery_result NOT NULL,
4789+
response_status INT2,
4790+
response_duration INTERVAL,
4791+
time_created TIMESTAMPTZ NOT NULL,
4792+
4793+
CONSTRAINT response_iff_not_unreachable CHECK (
4794+
(
4795+
-- If the result is 'succeedeed' or 'failed_http_error', response
4796+
-- data must be present.
4797+
(result = 'succeeded' OR result = 'failed_http_error') AND (
4798+
response_status IS NOT NULL AND
4799+
response_duration IS NOT NULL
4800+
)
4801+
) OR (
4802+
-- If the result is 'failed_unreachable', no response data is
4803+
-- present.
4804+
(result = 'failed_unreachable') AND (
4805+
response_status IS NULL AND
4806+
response_duration IS NULL
4807+
)
4808+
)
4809+
)
4810+
);
4811+
4812+
CREATE INDEX IF NOT EXISTS lookup_webhook_delivery_attempts_for_msg
4813+
ON omicron.public.webhook_msg_delivery_attempts (
4814+
dispatch_id
4815+
);
4816+
46864817
/*
46874818
* Keep this at the end of file so that the database does not contain a version
46884819
* until it is fully populated.

0 commit comments

Comments
 (0)