perf(db): use pk id for lookups and ordering (#390)

summary
- use the indexed primary key (`id`) for `getbyid`, `updateserver`, and
list ordering/cursor, instead of extracting `_meta...id` from jsonb
- in `updateserver`, error if the body tries to change the id; set
`meta.official.id` when missing

changes
- database: `getbyid` → `where id = $1`
- database: `updateserver` → `where id = $2`; fail on id mismatch; set
id if missing
- database: `list` orders by `id` and paginates via `id > $cursor`
- no schema or api changes

<details>
<summary>performance</summary>

- env: repo docker compose postgres, 5k–10k rows, warm cache
- getbyid: `id` ≈ 0.02 ms vs jsonb path ≈ 0.80 ms
- update: `id` ≈ 0.15 ms vs jsonb path ≈ 0.78 ms
- list (limit 100): order by `id` ≈ 0.13 ms vs jsonb path id ≈ 2.24 ms
(~17x slower)

</details>

<details>
<summary>explain analyze snippets</summary>

getbyid by `id`
```
Index Scan using idx_benchmark_servers_id on benchmark_servers (actual time≈0.01–0.02 ms)
Buffers: shared hit≈3
```

getbyid by jsonb path
```
Seq Scan on benchmark_servers (actual time≈0.43–0.83 ms)
Filter: ((value->'_meta'->'io.modelcontextprotocol.registry/official'->>'id') = $1)
Rows Removed by Filter: ≈4999
Buffers: shared hit≈455
```

update by `id`
```
Update on benchmark_servers …
-> Index Scan using idx_benchmark_servers_id … (actual time≈0.007 ms)
Execution Time: ≈0.15 ms
```

update by jsonb path
```
Update on benchmark_servers …
-> Seq Scan on benchmark_servers … Rows Removed by Filter: ≈4999
Execution Time: ≈0.78 ms
```
</details>

<details>
<summary>reproduce quickly</summary>

- start postgres: `docker compose up -d postgres`
- create a simple table (id varchar pk, value jsonb) with several
thousand rows
- compare plans: `explain (analyze, buffers) select value from … where
id = $1` vs the jsonb path predicate
</details>

---------

Co-authored-by: Adam Jones <adamj@anthropic.com>
This commit is contained in:
nate nowack 2025-09-09 10:10:55 -05:00 • committed by GitHub
commit 72f9f80d68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -94,12 +94,12 @@ func (db *PostgreSQL) List(
}
}
// Add cursor pagination using registry metadata ID
// Add cursor pagination using primary key ID
if cursor != "" {
if _, err := uuid.Parse(cursor); err != nil {
return nil, "", fmt.Errorf("invalid cursor format: %w", err)
}
whereConditions = append(whereConditions, fmt.Sprintf("(value->'_meta'->'io.modelcontextprotocol.registry/official'->>'id') > $%d", argIndex))
whereConditions = append(whereConditions, fmt.Sprintf("id > $%d", argIndex))
args = append(args, cursor)
argIndex++
}
@ -112,12 +112,12 @@ func (db *PostgreSQL) List(
// Simple query on servers table
query := fmt.Sprintf(`
SELECT value
FROM servers
%s
ORDER BY (value->'_meta'->'io.modelcontextprotocol.registry/official'->>'id')
LIMIT $%d
`, whereClause, argIndex)
SELECT value
FROM servers
%s
ORDER BY id
LIMIT $%d
`, whereClause, argIndex)
args = append(args, limit)
rows, err := db.conn.Query(ctx, query, args...)
@ -168,7 +168,7 @@ func (db *PostgreSQL) GetByID(ctx context.Context, id string) (*apiv0.ServerJSON
query := `
SELECT value
FROM servers
WHERE (value->'_meta'->'io.modelcontextprotocol.registry/official'->>'id') = $1
WHERE id = $1
`
var valueJSON []byte
@ -230,6 +230,11 @@ func (db *PostgreSQL) UpdateServer(ctx context.Context, id string, server *apiv0
return nil, ctx.Err()
}
// Validate that meta structure exists and ID matches path
if server.Meta == nil || server.Meta.Official == nil || server.Meta.Official.ID != id {
return nil, fmt.Errorf("%w: io.modelcontextprotocol.registry/official.id must match path id (%s)", ErrInvalidInput, id)
}
// Marshal updated server
valueJSON, err := json.Marshal(server)
if err != nil {
@ -240,7 +245,7 @@ func (db *PostgreSQL) UpdateServer(ctx context.Context, id string, server *apiv0
query := `
UPDATE servers
SET value = $1
WHERE (value->'_meta'->'io.modelcontextprotocol.registry/official'->>'id') = $2
WHERE id = $2
`
result, err := db.conn.Exec(ctx, query, valueJSON, id)