@secondlayer/shared 6.28.0 → 6.28.1

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.
@@ -0,0 +1,64 @@
1
+ import { type Kysely, sql } from "kysely";
2
+ import { onChainPlane } from "../src/db/migration-role.ts";
3
+
4
+ /**
5
+ * Streams read-path indexes on `events` (chain plane / SOURCE).
6
+ *
7
+ * The Streams firehose filters raw `events` by `data->>'sender'`,
8
+ * `data->>'recipient'`, and `data->>'asset_identifier'` (see
9
+ * `streamsFilterPredicate` in packages/indexer/src/streams-events.ts). Without
10
+ * these the filtered scan is a `Seq Scan on events` over the cursor range —
11
+ * millions of rows on mainnet — and the response window times out. The
12
+ * `(block_height, type)` composite backs both the candidate-row scan and the
13
+ * per-block all-types ordinal CTE that replaces the old per-row correlated
14
+ * COUNT(*).
15
+ *
16
+ * Partial predicate is `(data->>'<field>') IS NOT NULL`, NOT `type IN (...)`:
17
+ * the query filters on equality (`data->>'sender' = $1`), which Postgres can
18
+ * prove implies `IS NOT NULL`, so the index is usable even when the caller
19
+ * filters by sender/recipient WITHOUT a `types=` (a `type IN (transfer types)`
20
+ * predicate would not be provably implied by the unfiltered `type IN (all)`
21
+ * candidate scan, and the planner would skip the index). Keeps the index
22
+ * compact (only rows that carry the field are indexed) while staying usable.
23
+ *
24
+ * `CREATE INDEX CONCURRENTLY` cannot run inside the migrate runner's tx; in
25
+ * prod build these manually with CONCURRENTLY first (matching the index name
26
+ * EXACTLY), then this migration is a no-op there via `IF NOT EXISTS`. On
27
+ * dev/staging the events table is small, so the brief lock is acceptable.
28
+ * `events` is a chain-plane table → DDL no-ops on the control DB under the
29
+ * source/target split.
30
+ */
31
+ export async function up(db: Kysely<unknown>): Promise<void> {
32
+ await onChainPlane(async () => {
33
+ await sql`
34
+ CREATE INDEX IF NOT EXISTS events_height_type_idx
35
+ ON events (block_height, type)
36
+ `.execute(db);
37
+ await sql`
38
+ CREATE INDEX IF NOT EXISTS events_sender_height_idx
39
+ ON events ((data->>'sender'), block_height)
40
+ WHERE (data->>'sender') IS NOT NULL
41
+ `.execute(db);
42
+ await sql`
43
+ CREATE INDEX IF NOT EXISTS events_recipient_height_idx
44
+ ON events ((data->>'recipient'), block_height)
45
+ WHERE (data->>'recipient') IS NOT NULL
46
+ `.execute(db);
47
+ await sql`
48
+ CREATE INDEX IF NOT EXISTS events_asset_identifier_height_idx
49
+ ON events ((data->>'asset_identifier'), block_height)
50
+ WHERE (data->>'asset_identifier') IS NOT NULL
51
+ `.execute(db);
52
+ });
53
+ }
54
+
55
+ export async function down(db: Kysely<unknown>): Promise<void> {
56
+ await onChainPlane(async () => {
57
+ await sql`DROP INDEX IF EXISTS events_asset_identifier_height_idx`.execute(
58
+ db,
59
+ );
60
+ await sql`DROP INDEX IF EXISTS events_recipient_height_idx`.execute(db);
61
+ await sql`DROP INDEX IF EXISTS events_sender_height_idx`.execute(db);
62
+ await sql`DROP INDEX IF EXISTS events_height_type_idx`.execute(db);
63
+ });
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "6.28.0",
3
+ "version": "6.28.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",