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:
parent
7ed56bafa1
commit
72f9f80d68
1 changed files with 15 additions and 10 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue