@backstage/plugin-events-backend 0.6.7 → 0.6.8-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @backstage/plugin-events-backend
|
|
2
2
|
|
|
3
|
+
## 0.6.8-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a1ca033: Fixed the database event bus listener connection inheriting the trace context of the request that first opened it. Its keepalive queries are now recorded as separate root spans instead of being attached to that request's trace for as long as the connection stays open.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/backend-plugin-api@1.11.1-next.0
|
|
10
|
+
- @backstage/plugin-events-node@0.4.28-next.0
|
|
11
|
+
- @backstage/backend-openapi-utils@0.7.4-next.0
|
|
12
|
+
|
|
3
13
|
## 0.6.7
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
4
|
var errors = require('@backstage/errors');
|
|
5
5
|
var types = require('@backstage/types');
|
|
6
|
+
var api = require('@opentelemetry/api');
|
|
6
7
|
|
|
7
8
|
const WINDOW_MAX_COUNT_DEFAULT = 1e4;
|
|
8
9
|
const WINDOW_MIN_AGE_DEFAULT = { minutes: 10 };
|
|
@@ -132,7 +133,7 @@ class DatabaseEventBusListener {
|
|
|
132
133
|
await this.#connPromise;
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
+
const connect = api.context.bind(api.ROOT_CONTEXT, async () => {
|
|
136
137
|
const conn = await this.#client.acquireRawConnection();
|
|
137
138
|
try {
|
|
138
139
|
await conn.query(`LISTEN ${TOPIC_PUBLISH}`);
|
|
@@ -167,6 +168,7 @@ class DatabaseEventBusListener {
|
|
|
167
168
|
throw error;
|
|
168
169
|
}
|
|
169
170
|
});
|
|
171
|
+
this.#connPromise = Promise.resolve().then(connect);
|
|
170
172
|
try {
|
|
171
173
|
await this.#connPromise;
|
|
172
174
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatabaseEventBusStore.cjs.js","sources":["../../../src/service/hub/DatabaseEventBusStore.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { EventParams } from '@backstage/plugin-events-node';\nimport { EventBusStore } from './types';\nimport { Knex } from 'knex';\nimport {\n BackstageCredentials,\n BackstageServicePrincipal,\n DatabaseService,\n LifecycleService,\n LoggerService,\n SchedulerService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { ForwardedError, NotFoundError } from '@backstage/errors';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\n\nconst WINDOW_MAX_COUNT_DEFAULT = 10_000;\nconst WINDOW_MIN_AGE_DEFAULT = { minutes: 10 };\nconst WINDOW_MAX_AGE_DEFAULT = { days: 1 };\n\nconst MAX_BATCH_SIZE = 10;\nconst CLEANUP_BATCH_SIZE = 1_000;\nconst CLEANUP_STATEMENT_TIMEOUT_MS = 20_000;\nconst CLEANUP_RUN_DURATION_MS = 50_000;\nconst LISTENER_CONNECTION_TIMEOUT_MS = 60_000;\nconst KEEPALIVE_INTERVAL_MS = 60_000;\n\nconst TABLE_EVENTS = 'event_bus_events';\nconst TABLE_SUBSCRIPTIONS = 'event_bus_subscriptions';\nconst TOPIC_PUBLISH = 'event_bus_publish';\n\ntype EventsRow = {\n id: string;\n created_by: string;\n created_at: Date;\n topic: string;\n data_json: string;\n notified_subscribers: string[];\n};\n\ntype SubscriptionsRow = {\n id: string;\n created_by: string;\n created_at: Date;\n updated_at: Date;\n read_until: string;\n topics: string[];\n};\n\nfunction creatorId(\n credentials: BackstageCredentials<BackstageServicePrincipal>,\n) {\n return `service=${credentials.principal.subject}`;\n}\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-events-backend',\n 'migrations',\n);\n\ninterface InternalDbClient {\n acquireRawConnection(): Promise<InternalDbConnection>;\n destroyRawConnection(conn: InternalDbConnection): Promise<void>;\n}\n\ninterface InternalDbConnection {\n query(sql: string): Promise<void>;\n end(): Promise<void>;\n on(\n event: 'notification',\n listener: (event: { channel: string; payload: string }) => void,\n ): void;\n on(event: 'error', listener: (error: Error) => void): void;\n on(event: 'end', listener: (error?: Error) => void): void;\n removeAllListeners(): void;\n}\n\n// This internal class manages a single connection to the database that all listeners share\nclass DatabaseEventBusListener {\n readonly #client: InternalDbClient;\n readonly #logger: LoggerService;\n\n readonly #listeners = new Set<{\n topics: Set<string>;\n resolve: (result: { topic: string }) => void;\n reject: (error: Error) => void;\n }>();\n\n #isShuttingDown = false;\n #connPromise?: Promise<InternalDbConnection>;\n #connTimeout?: NodeJS.Timeout;\n #keepaliveInterval?: NodeJS.Timeout;\n\n constructor(client: InternalDbClient, logger: LoggerService) {\n this.#client = client;\n this.#logger = logger.child({ type: 'DatabaseEventBusListener' });\n }\n\n async setupListener(\n topics: Set<string>,\n signal: AbortSignal,\n ): Promise<{ waitForUpdate(): Promise<{ topic: string }> }> {\n if (this.#connTimeout) {\n clearTimeout(this.#connTimeout);\n this.#connTimeout = undefined;\n }\n\n await this.#ensureConnection();\n\n const updatePromise = new Promise<{ topic: string }>((resolve, reject) => {\n const listener = {\n topics,\n resolve(result: { topic: string }) {\n resolve(result);\n cleanup();\n },\n reject(err: Error) {\n reject(err);\n cleanup();\n },\n };\n this.#listeners.add(listener);\n\n const onAbort = () => {\n this.#listeners.delete(listener);\n this.#maybeTimeoutConnection();\n reject(signal.reason);\n cleanup();\n };\n\n function cleanup() {\n signal.removeEventListener('abort', onAbort);\n }\n\n signal.addEventListener('abort', onAbort);\n });\n\n // Ignore unhandled rejections\n updatePromise.catch(() => {});\n\n return { waitForUpdate: () => updatePromise };\n }\n\n async shutdown() {\n if (this.#isShuttingDown) {\n return;\n }\n this.#isShuttingDown = true;\n const conn = await this.#connPromise?.catch(() => undefined);\n if (conn) {\n this.#destroyConnection(conn);\n }\n }\n\n #handleNotify(topic: string) {\n this.#logger.debug(`Listener received notification for topic '${topic}'`);\n for (const l of this.#listeners) {\n if (l.topics.has(topic)) {\n l.resolve({ topic });\n this.#listeners.delete(l);\n }\n }\n this.#maybeTimeoutConnection();\n }\n\n // We don't try to reconnect on error, instead we notify all listeners and let\n // them try to establish a new connection\n #handleError(error: Error) {\n this.#logger.error(\n `Listener connection failed, notifying all listeners`,\n error,\n );\n for (const l of this.#listeners) {\n l.reject(new Error('Listener connection failed'));\n }\n this.#listeners.clear();\n this.#maybeTimeoutConnection();\n }\n\n #maybeTimeoutConnection() {\n // If we don't have any listeners, destroy the connection after a timeout\n if (this.#listeners.size === 0 && !this.#connTimeout) {\n this.#connTimeout = setTimeout(() => {\n this.#connTimeout = undefined;\n this.#connPromise?.then(conn => {\n this.#logger.info('Listener connection timed out, destroying');\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n });\n }, LISTENER_CONNECTION_TIMEOUT_MS);\n }\n }\n\n #destroyConnection(conn: InternalDbConnection) {\n if (this.#keepaliveInterval) {\n clearInterval(this.#keepaliveInterval);\n this.#keepaliveInterval = undefined;\n }\n this.#client.destroyRawConnection(conn).catch(error => {\n this.#logger.error(`Listener failed to destroy connection`, error);\n });\n conn.removeAllListeners();\n }\n\n async #ensureConnection() {\n if (this.#isShuttingDown) {\n throw new Error('Listener is shutting down');\n }\n if (this.#connPromise) {\n await this.#connPromise;\n return;\n }\n this.#connPromise = Promise.resolve().then(async () => {\n const conn = await this.#client.acquireRawConnection();\n\n try {\n await conn.query(`LISTEN ${TOPIC_PUBLISH}`);\n\n // Set up a keepalive interval to make sure the connection stays alive\n if (this.#keepaliveInterval) {\n clearInterval(this.#keepaliveInterval);\n }\n this.#keepaliveInterval = setInterval(() => {\n conn.query('select 1').catch(error => {\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n this.#handleError(new ForwardedError('Keepalive failed', error));\n });\n }, KEEPALIVE_INTERVAL_MS);\n\n conn.on('notification', event => {\n this.#handleNotify(event.payload);\n });\n conn.on('error', error => {\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n this.#handleError(error);\n });\n conn.on('end', error => {\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n this.#handleError(\n error ?? new Error('Connection ended unexpectedly'),\n );\n });\n return conn;\n } catch (error) {\n this.#destroyConnection(conn);\n throw error;\n }\n });\n try {\n await this.#connPromise;\n } catch (error) {\n this.#connPromise = undefined;\n throw error;\n }\n }\n}\n\nexport class DatabaseEventBusStore implements EventBusStore {\n static async create(options: {\n database: DatabaseService;\n logger: LoggerService;\n scheduler: SchedulerService;\n lifecycle: LifecycleService;\n window?: {\n /** Events within this range will never be deleted */\n minAge?: HumanDuration;\n /** Events outside of this age will always be deleted */\n maxAge?: HumanDuration;\n /** Events outside of this count will be deleted if they are outside the minAge window */\n maxCount?: number;\n };\n }): Promise<DatabaseEventBusStore> {\n const db = await options.database.getClient();\n\n if (db.client.config.client !== 'pg') {\n throw new Error(\n `DatabaseEventBusStore only supports PostgreSQL, got '${db.client.config.client}'`,\n );\n }\n\n if (!options.database.migrations?.skip) {\n await db.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n const listener = new DatabaseEventBusListener(db.client, options.logger);\n\n const store = new DatabaseEventBusStore(\n db,\n options.logger,\n listener,\n options.window?.maxCount ?? WINDOW_MAX_COUNT_DEFAULT,\n durationToMilliseconds(options.window?.minAge ?? WINDOW_MIN_AGE_DEFAULT),\n durationToMilliseconds(options.window?.maxAge ?? WINDOW_MAX_AGE_DEFAULT),\n );\n\n await options.scheduler.scheduleTask({\n id: 'event-bus-cleanup',\n scope: 'global',\n frequency: { minutes: 1 },\n timeout: { minutes: 1 },\n initialDelay: { seconds: 10 },\n fn: signal => store.#cleanup(signal),\n });\n\n options.lifecycle.addShutdownHook(async () => {\n await listener.shutdown();\n });\n\n return store;\n }\n\n /** @internal */\n static async forTest({\n db,\n logger,\n minAge = 0,\n maxAge = 10_000,\n }: {\n db: Knex;\n logger: LoggerService;\n minAge?: number;\n maxAge?: number;\n }) {\n await db.migrate.latest({ directory: migrationsDir });\n\n const store = new DatabaseEventBusStore(\n db,\n logger,\n new DatabaseEventBusListener(db.client, logger),\n 5,\n minAge,\n maxAge,\n );\n\n return Object.assign(store, {\n clean: (signal?: AbortSignal) => store.#cleanup(signal),\n });\n }\n\n readonly #db: Knex;\n readonly #logger: LoggerService;\n readonly #listener: DatabaseEventBusListener;\n readonly #windowMaxCount: number;\n readonly #windowMinAge: number;\n readonly #windowMaxAge: number;\n\n private constructor(\n db: Knex,\n logger: LoggerService,\n listener: DatabaseEventBusListener,\n windowMaxCount: number,\n windowMinAge: number,\n windowMaxAge: number,\n ) {\n this.#db = db;\n this.#logger = logger;\n this.#listener = listener;\n this.#windowMaxCount = windowMaxCount;\n this.#windowMinAge = windowMinAge;\n this.#windowMaxAge = windowMaxAge;\n }\n\n async publish(options: {\n event: EventParams;\n notifiedSubscribers?: string[];\n credentials: BackstageCredentials<BackstageServicePrincipal>;\n }): Promise<{ eventId: string } | undefined> {\n const topic = options.event.topic;\n const notifiedSubscribers = options.notifiedSubscribers ?? [];\n // This query inserts a new event into the database, but only if there are\n // subscribers to the topic that have not already been notified\n const result = await this.#db\n // There's no clean way to create a INSERT INTO .. SELECT with knex, so we end up with quite a lot of .raw(...)\n .into(\n this.#db.raw('?? (??, ??, ??, ??)', [\n TABLE_EVENTS,\n // These are the rows that we insert, and should match the SELECT below\n 'created_by',\n 'topic',\n 'data_json',\n 'notified_subscribers',\n ]),\n )\n .insert<EventsRow>(\n (q: Knex.QueryBuilder) =>\n q\n // We're not reading data to insert from anywhere else, just raw data\n .select(\n this.#db.raw('?', [creatorId(options.credentials)]),\n this.#db.raw('?', [topic]),\n this.#db.raw('?', [\n JSON.stringify({\n payload: options.event.eventPayload,\n metadata: options.event.metadata,\n }),\n ]),\n this.#db.raw('?', [notifiedSubscribers]),\n )\n // The rest of this query is to check whether there are any\n // subscribers that have not been notified yet\n .from(TABLE_SUBSCRIPTIONS)\n .whereNotIn('id', notifiedSubscribers) // Skip notified subscribers\n .andWhere(this.#db.raw('? = ANY(topics)', [topic])) // Match topic\n .having(this.#db.raw('count(*)'), '>', 0), // Check if there are any results\n )\n .returning<{ id: string }[]>('id');\n\n if (result.length === 0) {\n return undefined;\n }\n if (result.length > 1) {\n throw new Error(\n `Failed to insert event, unexpectedly updated ${result.length} rows`,\n );\n }\n\n const [{ id }] = result;\n\n // Notify other event bus instances that an event is available on the topic\n const notifyResult = await this.#db.select(\n this.#db.raw(`pg_notify(?, ?)`, [TOPIC_PUBLISH, topic]),\n );\n if (notifyResult?.length !== 1) {\n this.#logger.warn(\n `Failed to notify subscribers of event with ID '${id}' on topic '${topic}'`,\n );\n }\n\n return { eventId: id };\n }\n\n async upsertSubscription(\n id: string,\n topics: string[],\n credentials: BackstageCredentials<BackstageServicePrincipal>,\n ): Promise<void> {\n const [{ max: maxId }] = await this.#db(TABLE_EVENTS).max('id');\n const result = await this.#db<SubscriptionsRow>(TABLE_SUBSCRIPTIONS)\n .insert({\n id,\n created_by: creatorId(credentials),\n updated_at: this.#db.fn.now(),\n topics,\n read_until: maxId || 0,\n })\n .onConflict('id')\n .merge(['created_by', 'topics', 'updated_at'])\n .returning('*');\n\n if (result.length !== 1) {\n throw new Error(\n `Failed to upsert subscription, updated ${result.length} rows`,\n );\n }\n }\n\n async readSubscription(id: string): Promise<{ events: EventParams[] }> {\n // The below query selects the subscription we're reading from, locks it for\n // an update, reads events for the subscription up to the limit, and then\n // updates the pointer to the last read event.\n //\n // This is written as a plain SQL query to spare us all the horrors of\n // expressing this in knex.\n\n const { rows: result } = await this.#db.raw<{\n rows: [] | [{ events: EventsRow[] }];\n }>(\n `\n WITH subscription AS (\n SELECT topics, read_until\n FROM event_bus_subscriptions\n WHERE id = :id\n FOR UPDATE\n ),\n selected_events AS (\n SELECT event.*\n FROM subscription\n CROSS JOIN LATERAL (\n SELECT DISTINCT topic\n FROM unnest(subscription.topics) AS subscribed(topic)\n ) AS subscribed_topics\n CROSS JOIN LATERAL (\n SELECT event_bus_events.*\n FROM event_bus_events\n WHERE event_bus_events.topic = subscribed_topics.topic\n AND event_bus_events.id > subscription.read_until\n AND NOT :id = ANY(event_bus_events.notified_subscribers)\n ORDER BY event_bus_events.id ASC\n LIMIT :limit\n ) AS event\n ORDER BY event.id ASC\n LIMIT :limit\n ),\n last_event_id AS (\n SELECT max(id) AS last_event_id\n FROM selected_events\n ),\n events_array AS (\n SELECT json_agg(row_to_json(selected_events) ORDER BY id) AS events\n FROM selected_events\n )\n UPDATE event_bus_subscriptions\n SET read_until = COALESCE(last_event_id, (SELECT MAX(id) FROM event_bus_events), 0)\n FROM events_array, last_event_id\n WHERE event_bus_subscriptions.id = :id\n RETURNING events_array.events\n `,\n { id, limit: MAX_BATCH_SIZE },\n );\n\n if (result.length === 0) {\n throw new NotFoundError(`Subscription with ID '${id}' not found`);\n } else if (result.length > 1) {\n throw new Error(\n `Failed to read subscription, unexpectedly updated ${result.length} rows`,\n );\n }\n\n const rows = result[0].events;\n if (!rows || rows.length === 0) {\n return { events: [] };\n }\n\n return {\n events: rows.map(row => {\n const { payload, metadata } = JSON.parse(row.data_json);\n return {\n topic: row.topic,\n eventPayload: payload,\n metadata,\n };\n }),\n };\n }\n\n async setupListener(\n subscriptionId: string,\n options: {\n signal: AbortSignal;\n },\n ): Promise<{ waitForUpdate(): Promise<{ topic: string }> }> {\n const result = await this.#db<SubscriptionsRow>(TABLE_SUBSCRIPTIONS)\n .select('topics')\n .where({ id: subscriptionId })\n .first();\n\n if (!result) {\n throw new NotFoundError(\n `Subscription with ID '${subscriptionId}' not found`,\n );\n }\n\n options.signal.throwIfAborted();\n\n return this.#listener.setupListener(\n new Set(result.topics ?? []),\n options.signal,\n );\n }\n\n async #cleanup(signal?: AbortSignal) {\n if (signal?.aborted) {\n return;\n }\n\n const stopAt = Date.now() + CLEANUP_RUN_DURATION_MS;\n let eventCount = 0;\n let subscriberCount = 0;\n\n const setStatementTimeout = async (trx: Knex.Transaction) => {\n const remainingMs = stopAt - Date.now();\n if (remainingMs <= 0) {\n return false;\n }\n\n await trx.raw(\n `SET LOCAL statement_timeout = '${Math.min(\n CLEANUP_STATEMENT_TIMEOUT_MS,\n remainingMs,\n )}ms'`,\n );\n return true;\n };\n\n const deleteEventBatch = (createdBefore: Date, maxId?: string) => {\n const bindings: (Date | string | number)[] = [createdBefore];\n if (maxId !== undefined) {\n bindings.push(maxId);\n }\n bindings.push(CLEANUP_BATCH_SIZE);\n\n return this.#db.transaction(async trx => {\n if (!(await setStatementTimeout(trx))) {\n return 0;\n }\n const result = await trx.raw<{ rowCount: number }>(\n `WITH candidates AS (\n SELECT id FROM event_bus_events\n WHERE created_at < ? ${maxId === undefined ? '' : 'AND id <= ?'}\n ORDER BY created_at, id\n LIMIT ? FOR UPDATE SKIP LOCKED\n )\n DELETE FROM event_bus_events\n WHERE id = ANY (ARRAY(SELECT id FROM candidates))`,\n bindings,\n );\n return result.rowCount;\n });\n };\n\n const deleteSubscriberBatch = () =>\n this.#db.transaction<number>(async trx => {\n if (!(await setStatementTimeout(trx))) {\n return 0;\n }\n const [{ min: minId }] = await trx(TABLE_EVENTS).min('id');\n\n if (!(await setStatementTimeout(trx))) {\n return 0;\n }\n\n const where =\n minId === null\n ? 'updated_at < ?'\n : 'read_until < CAST(? AS bigint) - 1';\n const binding =\n minId === null ? new Date(Date.now() - this.#windowMaxAge) : minId;\n const result = await trx.raw<{ rowCount: number }>(\n `WITH candidates AS (\n SELECT id FROM event_bus_subscriptions\n WHERE ${where}\n ORDER BY id\n LIMIT ? FOR UPDATE SKIP LOCKED\n )\n DELETE FROM event_bus_subscriptions\n WHERE id = ANY (ARRAY(SELECT id FROM candidates))`,\n [binding, CLEANUP_BATCH_SIZE],\n );\n return result.rowCount;\n });\n\n // Expire events past maxAge first, then trim older events past maxCount.\n let eventCleanupPhase: 'maxAge' | 'maxCount' | 'complete' = 'maxAge';\n let countCutoff: string | undefined;\n let failed = false;\n\n // Finish one bounded event batch and one bounded subscriber batch before\n // checking cancellation or the run budget, so neither can starve the other.\n while (!failed && !signal?.aborted && Date.now() < stopAt) {\n let subscriberDeleted = 0;\n\n try {\n if (eventCleanupPhase === 'maxAge') {\n const deleted = await deleteEventBatch(\n new Date(Date.now() - this.#windowMaxAge),\n );\n eventCount += deleted;\n if (deleted < CLEANUP_BATCH_SIZE) {\n eventCleanupPhase = 'maxCount';\n }\n } else if (eventCleanupPhase === 'maxCount') {\n if (countCutoff === undefined) {\n const cutoff = await this.#db.transaction(async trx => {\n if (!(await setStatementTimeout(trx))) {\n return undefined;\n }\n return trx<EventsRow>(TABLE_EVENTS)\n .select('id')\n .orderBy('id', 'desc')\n .offset(this.#windowMaxCount)\n .first();\n });\n countCutoff = cutoff?.id;\n }\n\n if (countCutoff === undefined) {\n eventCleanupPhase = 'complete';\n } else {\n const deleted = await deleteEventBatch(\n new Date(Date.now() - this.#windowMinAge),\n countCutoff,\n );\n eventCount += deleted;\n if (deleted < CLEANUP_BATCH_SIZE) {\n eventCleanupPhase = 'complete';\n }\n }\n }\n } catch (error) {\n this.#logger.error('Event cleanup failed', error);\n failed = true;\n }\n\n try {\n subscriberDeleted = await deleteSubscriberBatch();\n subscriberCount += subscriberDeleted;\n } catch (error) {\n this.#logger.error('Subscription cleanup failed', error);\n failed = true;\n }\n\n if (\n eventCleanupPhase === 'complete' &&\n subscriberDeleted < CLEANUP_BATCH_SIZE\n ) {\n break;\n }\n }\n\n if (eventCount > 0) {\n this.#logger.info(\n `Event cleanup resulted in ${eventCount} old events being deleted`,\n );\n }\n if (subscriberCount > 0) {\n this.#logger.info(\n `Subscription cleanup resulted in ${subscriberCount} stale subscribers being deleted`,\n );\n }\n }\n}\n"],"names":["resolvePackagePath","ForwardedError","durationToMilliseconds","NotFoundError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DatabaseEventBusStore.cjs.js","sources":["../../../src/service/hub/DatabaseEventBusStore.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { EventParams } from '@backstage/plugin-events-node';\nimport { EventBusStore } from './types';\nimport { Knex } from 'knex';\nimport {\n BackstageCredentials,\n BackstageServicePrincipal,\n DatabaseService,\n LifecycleService,\n LoggerService,\n SchedulerService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { ForwardedError, NotFoundError } from '@backstage/errors';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { ROOT_CONTEXT, context } from '@opentelemetry/api';\n\nconst WINDOW_MAX_COUNT_DEFAULT = 10_000;\nconst WINDOW_MIN_AGE_DEFAULT = { minutes: 10 };\nconst WINDOW_MAX_AGE_DEFAULT = { days: 1 };\n\nconst MAX_BATCH_SIZE = 10;\nconst CLEANUP_BATCH_SIZE = 1_000;\nconst CLEANUP_STATEMENT_TIMEOUT_MS = 20_000;\nconst CLEANUP_RUN_DURATION_MS = 50_000;\nconst LISTENER_CONNECTION_TIMEOUT_MS = 60_000;\nconst KEEPALIVE_INTERVAL_MS = 60_000;\n\nconst TABLE_EVENTS = 'event_bus_events';\nconst TABLE_SUBSCRIPTIONS = 'event_bus_subscriptions';\nconst TOPIC_PUBLISH = 'event_bus_publish';\n\ntype EventsRow = {\n id: string;\n created_by: string;\n created_at: Date;\n topic: string;\n data_json: string;\n notified_subscribers: string[];\n};\n\ntype SubscriptionsRow = {\n id: string;\n created_by: string;\n created_at: Date;\n updated_at: Date;\n read_until: string;\n topics: string[];\n};\n\nfunction creatorId(\n credentials: BackstageCredentials<BackstageServicePrincipal>,\n) {\n return `service=${credentials.principal.subject}`;\n}\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-events-backend',\n 'migrations',\n);\n\ninterface InternalDbClient {\n acquireRawConnection(): Promise<InternalDbConnection>;\n destroyRawConnection(conn: InternalDbConnection): Promise<void>;\n}\n\ninterface InternalDbConnection {\n query(sql: string): Promise<void>;\n end(): Promise<void>;\n on(\n event: 'notification',\n listener: (event: { channel: string; payload: string }) => void,\n ): void;\n on(event: 'error', listener: (error: Error) => void): void;\n on(event: 'end', listener: (error?: Error) => void): void;\n removeAllListeners(): void;\n}\n\n// This internal class manages a single connection to the database that all listeners share\nclass DatabaseEventBusListener {\n readonly #client: InternalDbClient;\n readonly #logger: LoggerService;\n\n readonly #listeners = new Set<{\n topics: Set<string>;\n resolve: (result: { topic: string }) => void;\n reject: (error: Error) => void;\n }>();\n\n #isShuttingDown = false;\n #connPromise?: Promise<InternalDbConnection>;\n #connTimeout?: NodeJS.Timeout;\n #keepaliveInterval?: NodeJS.Timeout;\n\n constructor(client: InternalDbClient, logger: LoggerService) {\n this.#client = client;\n this.#logger = logger.child({ type: 'DatabaseEventBusListener' });\n }\n\n async setupListener(\n topics: Set<string>,\n signal: AbortSignal,\n ): Promise<{ waitForUpdate(): Promise<{ topic: string }> }> {\n if (this.#connTimeout) {\n clearTimeout(this.#connTimeout);\n this.#connTimeout = undefined;\n }\n\n await this.#ensureConnection();\n\n const updatePromise = new Promise<{ topic: string }>((resolve, reject) => {\n const listener = {\n topics,\n resolve(result: { topic: string }) {\n resolve(result);\n cleanup();\n },\n reject(err: Error) {\n reject(err);\n cleanup();\n },\n };\n this.#listeners.add(listener);\n\n const onAbort = () => {\n this.#listeners.delete(listener);\n this.#maybeTimeoutConnection();\n reject(signal.reason);\n cleanup();\n };\n\n function cleanup() {\n signal.removeEventListener('abort', onAbort);\n }\n\n signal.addEventListener('abort', onAbort);\n });\n\n // Ignore unhandled rejections\n updatePromise.catch(() => {});\n\n return { waitForUpdate: () => updatePromise };\n }\n\n async shutdown() {\n if (this.#isShuttingDown) {\n return;\n }\n this.#isShuttingDown = true;\n const conn = await this.#connPromise?.catch(() => undefined);\n if (conn) {\n this.#destroyConnection(conn);\n }\n }\n\n #handleNotify(topic: string) {\n this.#logger.debug(`Listener received notification for topic '${topic}'`);\n for (const l of this.#listeners) {\n if (l.topics.has(topic)) {\n l.resolve({ topic });\n this.#listeners.delete(l);\n }\n }\n this.#maybeTimeoutConnection();\n }\n\n // We don't try to reconnect on error, instead we notify all listeners and let\n // them try to establish a new connection\n #handleError(error: Error) {\n this.#logger.error(\n `Listener connection failed, notifying all listeners`,\n error,\n );\n for (const l of this.#listeners) {\n l.reject(new Error('Listener connection failed'));\n }\n this.#listeners.clear();\n this.#maybeTimeoutConnection();\n }\n\n #maybeTimeoutConnection() {\n // If we don't have any listeners, destroy the connection after a timeout\n if (this.#listeners.size === 0 && !this.#connTimeout) {\n this.#connTimeout = setTimeout(() => {\n this.#connTimeout = undefined;\n this.#connPromise?.then(conn => {\n this.#logger.info('Listener connection timed out, destroying');\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n });\n }, LISTENER_CONNECTION_TIMEOUT_MS);\n }\n }\n\n #destroyConnection(conn: InternalDbConnection) {\n if (this.#keepaliveInterval) {\n clearInterval(this.#keepaliveInterval);\n this.#keepaliveInterval = undefined;\n }\n this.#client.destroyRawConnection(conn).catch(error => {\n this.#logger.error(`Listener failed to destroy connection`, error);\n });\n conn.removeAllListeners();\n }\n\n async #ensureConnection() {\n if (this.#isShuttingDown) {\n throw new Error('Listener is shutting down');\n }\n if (this.#connPromise) {\n await this.#connPromise;\n return;\n }\n // Connect in the root context, so that keepalive queries on this long-lived\n // connection are not attached to the trace of the request that opened it.\n const connect = context.bind(ROOT_CONTEXT, async () => {\n const conn = await this.#client.acquireRawConnection();\n\n try {\n await conn.query(`LISTEN ${TOPIC_PUBLISH}`);\n\n // Set up a keepalive interval to make sure the connection stays alive\n if (this.#keepaliveInterval) {\n clearInterval(this.#keepaliveInterval);\n }\n this.#keepaliveInterval = setInterval(() => {\n conn.query('select 1').catch(error => {\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n this.#handleError(new ForwardedError('Keepalive failed', error));\n });\n }, KEEPALIVE_INTERVAL_MS);\n\n conn.on('notification', event => {\n this.#handleNotify(event.payload);\n });\n conn.on('error', error => {\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n this.#handleError(error);\n });\n conn.on('end', error => {\n this.#connPromise = undefined;\n this.#destroyConnection(conn);\n this.#handleError(\n error ?? new Error('Connection ended unexpectedly'),\n );\n });\n return conn;\n } catch (error) {\n this.#destroyConnection(conn);\n throw error;\n }\n });\n this.#connPromise = Promise.resolve().then(connect);\n try {\n await this.#connPromise;\n } catch (error) {\n this.#connPromise = undefined;\n throw error;\n }\n }\n}\n\nexport class DatabaseEventBusStore implements EventBusStore {\n static async create(options: {\n database: DatabaseService;\n logger: LoggerService;\n scheduler: SchedulerService;\n lifecycle: LifecycleService;\n window?: {\n /** Events within this range will never be deleted */\n minAge?: HumanDuration;\n /** Events outside of this age will always be deleted */\n maxAge?: HumanDuration;\n /** Events outside of this count will be deleted if they are outside the minAge window */\n maxCount?: number;\n };\n }): Promise<DatabaseEventBusStore> {\n const db = await options.database.getClient();\n\n if (db.client.config.client !== 'pg') {\n throw new Error(\n `DatabaseEventBusStore only supports PostgreSQL, got '${db.client.config.client}'`,\n );\n }\n\n if (!options.database.migrations?.skip) {\n await db.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n const listener = new DatabaseEventBusListener(db.client, options.logger);\n\n const store = new DatabaseEventBusStore(\n db,\n options.logger,\n listener,\n options.window?.maxCount ?? WINDOW_MAX_COUNT_DEFAULT,\n durationToMilliseconds(options.window?.minAge ?? WINDOW_MIN_AGE_DEFAULT),\n durationToMilliseconds(options.window?.maxAge ?? WINDOW_MAX_AGE_DEFAULT),\n );\n\n await options.scheduler.scheduleTask({\n id: 'event-bus-cleanup',\n scope: 'global',\n frequency: { minutes: 1 },\n timeout: { minutes: 1 },\n initialDelay: { seconds: 10 },\n fn: signal => store.#cleanup(signal),\n });\n\n options.lifecycle.addShutdownHook(async () => {\n await listener.shutdown();\n });\n\n return store;\n }\n\n /** @internal */\n static async forTest({\n db,\n logger,\n minAge = 0,\n maxAge = 10_000,\n }: {\n db: Knex;\n logger: LoggerService;\n minAge?: number;\n maxAge?: number;\n }) {\n await db.migrate.latest({ directory: migrationsDir });\n\n const store = new DatabaseEventBusStore(\n db,\n logger,\n new DatabaseEventBusListener(db.client, logger),\n 5,\n minAge,\n maxAge,\n );\n\n return Object.assign(store, {\n clean: (signal?: AbortSignal) => store.#cleanup(signal),\n });\n }\n\n readonly #db: Knex;\n readonly #logger: LoggerService;\n readonly #listener: DatabaseEventBusListener;\n readonly #windowMaxCount: number;\n readonly #windowMinAge: number;\n readonly #windowMaxAge: number;\n\n private constructor(\n db: Knex,\n logger: LoggerService,\n listener: DatabaseEventBusListener,\n windowMaxCount: number,\n windowMinAge: number,\n windowMaxAge: number,\n ) {\n this.#db = db;\n this.#logger = logger;\n this.#listener = listener;\n this.#windowMaxCount = windowMaxCount;\n this.#windowMinAge = windowMinAge;\n this.#windowMaxAge = windowMaxAge;\n }\n\n async publish(options: {\n event: EventParams;\n notifiedSubscribers?: string[];\n credentials: BackstageCredentials<BackstageServicePrincipal>;\n }): Promise<{ eventId: string } | undefined> {\n const topic = options.event.topic;\n const notifiedSubscribers = options.notifiedSubscribers ?? [];\n // This query inserts a new event into the database, but only if there are\n // subscribers to the topic that have not already been notified\n const result = await this.#db\n // There's no clean way to create a INSERT INTO .. SELECT with knex, so we end up with quite a lot of .raw(...)\n .into(\n this.#db.raw('?? (??, ??, ??, ??)', [\n TABLE_EVENTS,\n // These are the rows that we insert, and should match the SELECT below\n 'created_by',\n 'topic',\n 'data_json',\n 'notified_subscribers',\n ]),\n )\n .insert<EventsRow>(\n (q: Knex.QueryBuilder) =>\n q\n // We're not reading data to insert from anywhere else, just raw data\n .select(\n this.#db.raw('?', [creatorId(options.credentials)]),\n this.#db.raw('?', [topic]),\n this.#db.raw('?', [\n JSON.stringify({\n payload: options.event.eventPayload,\n metadata: options.event.metadata,\n }),\n ]),\n this.#db.raw('?', [notifiedSubscribers]),\n )\n // The rest of this query is to check whether there are any\n // subscribers that have not been notified yet\n .from(TABLE_SUBSCRIPTIONS)\n .whereNotIn('id', notifiedSubscribers) // Skip notified subscribers\n .andWhere(this.#db.raw('? = ANY(topics)', [topic])) // Match topic\n .having(this.#db.raw('count(*)'), '>', 0), // Check if there are any results\n )\n .returning<{ id: string }[]>('id');\n\n if (result.length === 0) {\n return undefined;\n }\n if (result.length > 1) {\n throw new Error(\n `Failed to insert event, unexpectedly updated ${result.length} rows`,\n );\n }\n\n const [{ id }] = result;\n\n // Notify other event bus instances that an event is available on the topic\n const notifyResult = await this.#db.select(\n this.#db.raw(`pg_notify(?, ?)`, [TOPIC_PUBLISH, topic]),\n );\n if (notifyResult?.length !== 1) {\n this.#logger.warn(\n `Failed to notify subscribers of event with ID '${id}' on topic '${topic}'`,\n );\n }\n\n return { eventId: id };\n }\n\n async upsertSubscription(\n id: string,\n topics: string[],\n credentials: BackstageCredentials<BackstageServicePrincipal>,\n ): Promise<void> {\n const [{ max: maxId }] = await this.#db(TABLE_EVENTS).max('id');\n const result = await this.#db<SubscriptionsRow>(TABLE_SUBSCRIPTIONS)\n .insert({\n id,\n created_by: creatorId(credentials),\n updated_at: this.#db.fn.now(),\n topics,\n read_until: maxId || 0,\n })\n .onConflict('id')\n .merge(['created_by', 'topics', 'updated_at'])\n .returning('*');\n\n if (result.length !== 1) {\n throw new Error(\n `Failed to upsert subscription, updated ${result.length} rows`,\n );\n }\n }\n\n async readSubscription(id: string): Promise<{ events: EventParams[] }> {\n // The below query selects the subscription we're reading from, locks it for\n // an update, reads events for the subscription up to the limit, and then\n // updates the pointer to the last read event.\n //\n // This is written as a plain SQL query to spare us all the horrors of\n // expressing this in knex.\n\n const { rows: result } = await this.#db.raw<{\n rows: [] | [{ events: EventsRow[] }];\n }>(\n `\n WITH subscription AS (\n SELECT topics, read_until\n FROM event_bus_subscriptions\n WHERE id = :id\n FOR UPDATE\n ),\n selected_events AS (\n SELECT event.*\n FROM subscription\n CROSS JOIN LATERAL (\n SELECT DISTINCT topic\n FROM unnest(subscription.topics) AS subscribed(topic)\n ) AS subscribed_topics\n CROSS JOIN LATERAL (\n SELECT event_bus_events.*\n FROM event_bus_events\n WHERE event_bus_events.topic = subscribed_topics.topic\n AND event_bus_events.id > subscription.read_until\n AND NOT :id = ANY(event_bus_events.notified_subscribers)\n ORDER BY event_bus_events.id ASC\n LIMIT :limit\n ) AS event\n ORDER BY event.id ASC\n LIMIT :limit\n ),\n last_event_id AS (\n SELECT max(id) AS last_event_id\n FROM selected_events\n ),\n events_array AS (\n SELECT json_agg(row_to_json(selected_events) ORDER BY id) AS events\n FROM selected_events\n )\n UPDATE event_bus_subscriptions\n SET read_until = COALESCE(last_event_id, (SELECT MAX(id) FROM event_bus_events), 0)\n FROM events_array, last_event_id\n WHERE event_bus_subscriptions.id = :id\n RETURNING events_array.events\n `,\n { id, limit: MAX_BATCH_SIZE },\n );\n\n if (result.length === 0) {\n throw new NotFoundError(`Subscription with ID '${id}' not found`);\n } else if (result.length > 1) {\n throw new Error(\n `Failed to read subscription, unexpectedly updated ${result.length} rows`,\n );\n }\n\n const rows = result[0].events;\n if (!rows || rows.length === 0) {\n return { events: [] };\n }\n\n return {\n events: rows.map(row => {\n const { payload, metadata } = JSON.parse(row.data_json);\n return {\n topic: row.topic,\n eventPayload: payload,\n metadata,\n };\n }),\n };\n }\n\n async setupListener(\n subscriptionId: string,\n options: {\n signal: AbortSignal;\n },\n ): Promise<{ waitForUpdate(): Promise<{ topic: string }> }> {\n const result = await this.#db<SubscriptionsRow>(TABLE_SUBSCRIPTIONS)\n .select('topics')\n .where({ id: subscriptionId })\n .first();\n\n if (!result) {\n throw new NotFoundError(\n `Subscription with ID '${subscriptionId}' not found`,\n );\n }\n\n options.signal.throwIfAborted();\n\n return this.#listener.setupListener(\n new Set(result.topics ?? []),\n options.signal,\n );\n }\n\n async #cleanup(signal?: AbortSignal) {\n if (signal?.aborted) {\n return;\n }\n\n const stopAt = Date.now() + CLEANUP_RUN_DURATION_MS;\n let eventCount = 0;\n let subscriberCount = 0;\n\n const setStatementTimeout = async (trx: Knex.Transaction) => {\n const remainingMs = stopAt - Date.now();\n if (remainingMs <= 0) {\n return false;\n }\n\n await trx.raw(\n `SET LOCAL statement_timeout = '${Math.min(\n CLEANUP_STATEMENT_TIMEOUT_MS,\n remainingMs,\n )}ms'`,\n );\n return true;\n };\n\n const deleteEventBatch = (createdBefore: Date, maxId?: string) => {\n const bindings: (Date | string | number)[] = [createdBefore];\n if (maxId !== undefined) {\n bindings.push(maxId);\n }\n bindings.push(CLEANUP_BATCH_SIZE);\n\n return this.#db.transaction(async trx => {\n if (!(await setStatementTimeout(trx))) {\n return 0;\n }\n const result = await trx.raw<{ rowCount: number }>(\n `WITH candidates AS (\n SELECT id FROM event_bus_events\n WHERE created_at < ? ${maxId === undefined ? '' : 'AND id <= ?'}\n ORDER BY created_at, id\n LIMIT ? FOR UPDATE SKIP LOCKED\n )\n DELETE FROM event_bus_events\n WHERE id = ANY (ARRAY(SELECT id FROM candidates))`,\n bindings,\n );\n return result.rowCount;\n });\n };\n\n const deleteSubscriberBatch = () =>\n this.#db.transaction<number>(async trx => {\n if (!(await setStatementTimeout(trx))) {\n return 0;\n }\n const [{ min: minId }] = await trx(TABLE_EVENTS).min('id');\n\n if (!(await setStatementTimeout(trx))) {\n return 0;\n }\n\n const where =\n minId === null\n ? 'updated_at < ?'\n : 'read_until < CAST(? AS bigint) - 1';\n const binding =\n minId === null ? new Date(Date.now() - this.#windowMaxAge) : minId;\n const result = await trx.raw<{ rowCount: number }>(\n `WITH candidates AS (\n SELECT id FROM event_bus_subscriptions\n WHERE ${where}\n ORDER BY id\n LIMIT ? FOR UPDATE SKIP LOCKED\n )\n DELETE FROM event_bus_subscriptions\n WHERE id = ANY (ARRAY(SELECT id FROM candidates))`,\n [binding, CLEANUP_BATCH_SIZE],\n );\n return result.rowCount;\n });\n\n // Expire events past maxAge first, then trim older events past maxCount.\n let eventCleanupPhase: 'maxAge' | 'maxCount' | 'complete' = 'maxAge';\n let countCutoff: string | undefined;\n let failed = false;\n\n // Finish one bounded event batch and one bounded subscriber batch before\n // checking cancellation or the run budget, so neither can starve the other.\n while (!failed && !signal?.aborted && Date.now() < stopAt) {\n let subscriberDeleted = 0;\n\n try {\n if (eventCleanupPhase === 'maxAge') {\n const deleted = await deleteEventBatch(\n new Date(Date.now() - this.#windowMaxAge),\n );\n eventCount += deleted;\n if (deleted < CLEANUP_BATCH_SIZE) {\n eventCleanupPhase = 'maxCount';\n }\n } else if (eventCleanupPhase === 'maxCount') {\n if (countCutoff === undefined) {\n const cutoff = await this.#db.transaction(async trx => {\n if (!(await setStatementTimeout(trx))) {\n return undefined;\n }\n return trx<EventsRow>(TABLE_EVENTS)\n .select('id')\n .orderBy('id', 'desc')\n .offset(this.#windowMaxCount)\n .first();\n });\n countCutoff = cutoff?.id;\n }\n\n if (countCutoff === undefined) {\n eventCleanupPhase = 'complete';\n } else {\n const deleted = await deleteEventBatch(\n new Date(Date.now() - this.#windowMinAge),\n countCutoff,\n );\n eventCount += deleted;\n if (deleted < CLEANUP_BATCH_SIZE) {\n eventCleanupPhase = 'complete';\n }\n }\n }\n } catch (error) {\n this.#logger.error('Event cleanup failed', error);\n failed = true;\n }\n\n try {\n subscriberDeleted = await deleteSubscriberBatch();\n subscriberCount += subscriberDeleted;\n } catch (error) {\n this.#logger.error('Subscription cleanup failed', error);\n failed = true;\n }\n\n if (\n eventCleanupPhase === 'complete' &&\n subscriberDeleted < CLEANUP_BATCH_SIZE\n ) {\n break;\n }\n }\n\n if (eventCount > 0) {\n this.#logger.info(\n `Event cleanup resulted in ${eventCount} old events being deleted`,\n );\n }\n if (subscriberCount > 0) {\n this.#logger.info(\n `Subscription cleanup resulted in ${subscriberCount} stale subscribers being deleted`,\n );\n }\n }\n}\n"],"names":["resolvePackagePath","context","ROOT_CONTEXT","ForwardedError","durationToMilliseconds","NotFoundError"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-events-backend",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8-next.0",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "backend-plugin",
|
|
6
6
|
"pluginId": "events",
|
|
@@ -67,12 +67,13 @@
|
|
|
67
67
|
"test": "backstage-cli package test"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@backstage/backend-openapi-utils": "
|
|
71
|
-
"@backstage/backend-plugin-api": "
|
|
72
|
-
"@backstage/config": "
|
|
73
|
-
"@backstage/errors": "
|
|
74
|
-
"@backstage/plugin-events-node": "
|
|
75
|
-
"@backstage/types": "
|
|
70
|
+
"@backstage/backend-openapi-utils": "0.7.4-next.0",
|
|
71
|
+
"@backstage/backend-plugin-api": "1.11.1-next.0",
|
|
72
|
+
"@backstage/config": "1.3.10",
|
|
73
|
+
"@backstage/errors": "1.3.2",
|
|
74
|
+
"@backstage/plugin-events-node": "0.4.28-next.0",
|
|
75
|
+
"@backstage/types": "1.2.2",
|
|
76
|
+
"@opentelemetry/api": "^1.9.0",
|
|
76
77
|
"@types/express": "^4.17.6",
|
|
77
78
|
"content-type": "^1.0.5",
|
|
78
79
|
"express": "^4.22.0",
|
|
@@ -80,12 +81,12 @@
|
|
|
80
81
|
"knex": "^3.0.0"
|
|
81
82
|
},
|
|
82
83
|
"devDependencies": {
|
|
83
|
-
"@backstage/backend-app-api": "
|
|
84
|
-
"@backstage/backend-defaults": "
|
|
85
|
-
"@backstage/backend-test-utils": "
|
|
86
|
-
"@backstage/cli": "
|
|
87
|
-
"@backstage/plugin-events-backend-test-utils": "
|
|
88
|
-
"@backstage/repo-tools": "
|
|
84
|
+
"@backstage/backend-app-api": "1.9.0-next.0",
|
|
85
|
+
"@backstage/backend-defaults": "0.18.2-next.0",
|
|
86
|
+
"@backstage/backend-test-utils": "1.11.9-next.0",
|
|
87
|
+
"@backstage/cli": "0.36.8-next.0",
|
|
88
|
+
"@backstage/plugin-events-backend-test-utils": "0.1.61-next.0",
|
|
89
|
+
"@backstage/repo-tools": "0.19.3-next.0",
|
|
89
90
|
"@types/content-type": "^1.1.8",
|
|
90
91
|
"supertest": "^7.0.0"
|
|
91
92
|
},
|