@bsv/overlay 2.2.0 → 2.2.7
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/LICENSE.txt +57 -27
- package/README.md +102 -266
- package/dist/cjs/mod.d.ts +15 -0
- package/dist/cjs/mod.d.ts.map +1 -0
- package/dist/cjs/mod.js +4 -1
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +46 -18
- package/dist/cjs/src/Advertisement.d.ts +9 -0
- package/dist/cjs/src/Advertisement.d.ts.map +1 -0
- package/dist/cjs/src/Advertiser.d.ts +36 -0
- package/dist/cjs/src/Advertiser.d.ts.map +1 -0
- package/dist/cjs/src/BASM.d.ts +110 -0
- package/dist/cjs/src/BASM.d.ts.map +1 -0
- package/dist/cjs/src/BASM.js.map +1 -1
- package/dist/cjs/src/BASMRemote.d.ts +14 -0
- package/dist/cjs/src/BASMRemote.d.ts.map +1 -0
- package/dist/cjs/src/BASMRemote.js.map +1 -1
- package/dist/cjs/src/Engine.d.ts +377 -0
- package/dist/cjs/src/Engine.d.ts.map +1 -0
- package/dist/cjs/src/Engine.js +430 -436
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +24 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.js +7 -10
- package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +103 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.js +34 -31
- package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/cjs/src/LookupFormula.d.ts +29 -0
- package/dist/cjs/src/LookupFormula.d.ts.map +1 -0
- package/dist/cjs/src/LookupService.d.ts +85 -0
- package/dist/cjs/src/LookupService.d.ts.map +1 -0
- package/dist/cjs/src/Output.d.ts +32 -0
- package/dist/cjs/src/Output.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.d.ts +14 -0
- package/dist/cjs/src/SafeLog.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.js +44 -0
- package/dist/cjs/src/SafeLog.js.map +1 -0
- package/{src/SyncConfiguration.ts → dist/cjs/src/SyncConfiguration.d.ts} +2 -1
- package/dist/cjs/src/SyncConfiguration.d.ts.map +1 -0
- package/dist/cjs/src/TopicManager.d.ts +35 -0
- package/dist/cjs/src/TopicManager.d.ts.map +1 -0
- package/dist/cjs/src/storage/Storage.d.ts +237 -0
- package/dist/cjs/src/storage/Storage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts +69 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.js +7 -10
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/all-migrations.d.ts +11 -0
- package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js +36 -12
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts +9 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -1
- package/dist/esm/mod.js +2 -1
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/BASMRemote.js.map +1 -1
- package/dist/esm/src/Engine.js +408 -414
- package/dist/esm/src/Engine.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -2
- package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPStorage.js +23 -19
- package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/esm/src/SafeLog.js +40 -0
- package/dist/esm/src/SafeLog.js.map +1 -0
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js +36 -12
- package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
- package/dist/types/mod.d.ts +5 -4
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/BASMRemote.d.ts.map +1 -1
- package/dist/types/src/Engine.d.ts +15 -0
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts +2 -2
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts +3 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
- package/dist/types/src/SafeLog.d.ts +14 -0
- package/dist/types/src/SafeLog.d.ts.map +1 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
- package/dist/types/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -1
- package/package.json +87 -30
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
- package/dist/types/tsconfig.types.tsbuildinfo +0 -1
- package/docs/API.md +0 -944
- package/docs/BRC-136-BASM.md +0 -327
- package/docs/README.md +0 -16
- package/docs/Synchronization.md +0 -212
- package/docs/concepts/00-overview.md +0 -85
- package/docs/concepts/01-best-practices.md +0 -202
- package/docs/concepts/02-query-performance.md +0 -345
- package/docs/concepts/03-database-monitoring.md +0 -211
- package/docs/concepts/04-pagination-example.md +0 -186
- package/docs/concepts/05-recommendations-summary.md +0 -158
- package/docs/concepts/README.md +0 -20
- package/docs/examples/README.md +0 -15
- package/docs/examples/gs-wip.md +0 -111
- package/docs/internal/README.md +0 -11
- package/mod.ts +0 -40
- package/src/Advertisement.ts +0 -8
- package/src/Advertiser.ts +0 -41
- package/src/BASM.ts +0 -208
- package/src/BASMRemote.ts +0 -54
- package/src/Engine.ts +0 -2199
- package/src/GASP/OverlayGASPRemote.ts +0 -108
- package/src/GASP/OverlayGASPStorage.ts +0 -383
- package/src/LookupFormula.ts +0 -31
- package/src/LookupService.ts +0 -136
- package/src/Output.ts +0 -31
- package/src/TopicManager.ts +0 -40
- package/src/__tests/BASM.test.ts +0 -44
- package/src/__tests/BASMChain.test.ts +0 -290
- package/src/__tests/Engine.test.ts +0 -1345
- package/src/__tests/OverlayGASPRemote.test.ts +0 -135
- package/src/__tests/OverlayGASPStorage.test.ts +0 -171
- package/src/storage/Storage.ts +0 -260
- package/src/storage/knex/KnexStorage.ts +0 -651
- package/src/storage/knex/all-migrations.ts +0 -32
- package/src/storage/knex/migrations/2024-05-18-001-initial.ts +0 -30
- package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +0 -15
- package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +0 -40
- package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +0 -30
- package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +0 -42
- package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +0 -46
- package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +0 -24
- package/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.ts +0 -18
- package/src/storage/knex/migrations/2026-05-29-001-brc136-basm.ts +0 -123
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
# Overlay Best Practices for Developers
|
|
2
|
-
|
|
3
|
-
[🏠 Home](../README.md) | [📚 API](../API.md) | [💡 Concepts](./README.md) | [📖 Examples](../examples/README.md) | [⚙️ Internal](../internal/README.md)
|
|
4
|
-
|
|
5
|
-
**Navigation:** [Overview](./00-overview.md) | [Best Practices](./01-best-practices.md) | [Query Performance](./02-query-performance.md) | [Database Monitoring](./03-database-monitoring.md) | [Pagination Example](./04-pagination-example.md) | [Recommendations Summary](./05-recommendations-summary.md)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
This document provides **best practices for designing and maintaining overlay services** in the BSV ecosystem. It focuses on the practical concerns of developers who are building their **own overlays**—not on BSVA internals or modifications to Overlay Express.
|
|
12
|
-
|
|
13
|
-
The goal is to help developers make consistent and scalable decisions when implementing **Topic Managers**, **Lookup Services**, and associated **MongoDB storage layers**. Examples reference **Convo Messenger**, but the principles apply to all overlay-based systems.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 1. Data Storage Principles
|
|
18
|
-
|
|
19
|
-
Overlay Lookup Services should store **small, structured metadata**, not entire payloads. The goal is fast, predictable querying.
|
|
20
|
-
|
|
21
|
-
### 1.1 Required Fields for Any Overlay
|
|
22
|
-
|
|
23
|
-
Every Lookup Service must store enough information to uniquely reference an on-chain output. This includes:
|
|
24
|
-
|
|
25
|
-
* **`txid`** — transaction ID containing the output
|
|
26
|
-
* **`outputIndex`** — index of the admitted output
|
|
27
|
-
* **`protocol`** — identifies which overlay protocol this entry belongs to
|
|
28
|
-
|
|
29
|
-
These fields ensure that applications can always locate the exact on-chain output referenced by the overlay.
|
|
30
|
-
|
|
31
|
-
### 1.2 Common Metadata Fields
|
|
32
|
-
|
|
33
|
-
In addition to required fields, overlays typically store small metadata extracted from PushDrop fields or derived from application logic.
|
|
34
|
-
|
|
35
|
-
Examples:
|
|
36
|
-
|
|
37
|
-
* **`timestamp` / `createdAt`** — when the message or record was created
|
|
38
|
-
* **`sender`** — sender's pubkey or DID
|
|
39
|
-
* **`threadId`** — group/thread identifier
|
|
40
|
-
* **`parentMessageId`** (optional) — for reply messages
|
|
41
|
-
* **reaction fields** (when applicable)
|
|
42
|
-
|
|
43
|
-
Example (Convo Messenger):
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
{
|
|
47
|
-
txid: "abc123...",
|
|
48
|
-
outputIndex: 0,
|
|
49
|
-
protocol: [2, "convo"],
|
|
50
|
-
threadId: "thread-xyz",
|
|
51
|
-
sender: "035a1b...",
|
|
52
|
-
createdAt: 1730000000000,
|
|
53
|
-
encryptedPayload: [...], // ciphertext array
|
|
54
|
-
header: [...], // CurvePoint header
|
|
55
|
-
uniqueId: "optional-value",
|
|
56
|
-
parentMessageId: "optional-parent-txid"
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**Note:** Convo stores *encryptedPayload* and *header* because these remain small arrays needed by the client. Overlays must avoid storing anything large (e.g., full media files).
|
|
61
|
-
|
|
62
|
-
### 1.3 When to Use UHRP
|
|
63
|
-
|
|
64
|
-
If an overlay needs to handle large data (text bodies, images, files), Lookup Services should **only store a UHRP reference**:
|
|
65
|
-
|
|
66
|
-
* Never store full file contents
|
|
67
|
-
* Never store large plaintext or ciphertext blobs
|
|
68
|
-
* Let the client fetch large data from UHRP when needed
|
|
69
|
-
|
|
70
|
-
Lookup Services should remain lightweight.
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 2. What Lookup Services Should *Not* Store
|
|
75
|
-
|
|
76
|
-
To maintain performance and predictable scaling, Lookup Services must avoid:
|
|
77
|
-
|
|
78
|
-
* Full PushDrop payloads or full scripts
|
|
79
|
-
* Large encrypted or plaintext message bodies
|
|
80
|
-
* Media files or binary attachments
|
|
81
|
-
* Identity certificates
|
|
82
|
-
* Redundant or duplicate on-chain data
|
|
83
|
-
|
|
84
|
-
**Rule of thumb:**
|
|
85
|
-
If it cannot be indexed efficiently, it does not belong in the Lookup DB.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## 3. Structuring Lookup Queries
|
|
90
|
-
|
|
91
|
-
### 3.1 Use Indexed, Selective Fields
|
|
92
|
-
|
|
93
|
-
All queries should filter using indexed, selective fields such as:
|
|
94
|
-
|
|
95
|
-
* `threadId`
|
|
96
|
-
* `sender`
|
|
97
|
-
* `parentMessageId`
|
|
98
|
-
* `createdAt`
|
|
99
|
-
|
|
100
|
-
Examples from Convo’s actual Mongo indexes:
|
|
101
|
-
|
|
102
|
-
```js
|
|
103
|
-
db.convoMessages.createIndex({ threadId: 1 })
|
|
104
|
-
db.convoMessages.createIndex({ parentMessageId: 1 })
|
|
105
|
-
db.convoMessages.createIndex({ threadId: 1, createdAt: -1 })
|
|
106
|
-
db.convoReactions.createIndex({ threadId: 1 })
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### 3.2 Use Pagination Everywhere
|
|
110
|
-
|
|
111
|
-
List endpoints must support:
|
|
112
|
-
|
|
113
|
-
* **`skip`** — starting offset
|
|
114
|
-
* **`limit`** — number of items
|
|
115
|
-
|
|
116
|
-
Typical defaults:
|
|
117
|
-
|
|
118
|
-
* `limit = 50`
|
|
119
|
-
* `skip = 0`
|
|
120
|
-
|
|
121
|
-
Example (Convo):
|
|
122
|
-
|
|
123
|
-
```ts
|
|
124
|
-
const messages = await this.storage.listThreadMessages(threadId, skip, limit)
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### 3.3 Avoid Expensive Query Patterns
|
|
128
|
-
|
|
129
|
-
Do **not**:
|
|
130
|
-
|
|
131
|
-
* Perform unbounded collection scans
|
|
132
|
-
* Query across all protocols
|
|
133
|
-
* Sort without an index
|
|
134
|
-
* Use `$regex` or `$text` filters
|
|
135
|
-
|
|
136
|
-
If complex queries are needed, break them into:
|
|
137
|
-
|
|
138
|
-
* Indexed filters
|
|
139
|
-
* Bounded pagination
|
|
140
|
-
* Incremental lookups
|
|
141
|
-
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
## 4. Topic Manager Best Practices
|
|
145
|
-
|
|
146
|
-
Topic Managers determine which outputs belong to your overlay. They must validate and parse data correctly.
|
|
147
|
-
|
|
148
|
-
Best practices:
|
|
149
|
-
|
|
150
|
-
1. **Validate admissible outputs** using PushDrop decoding.
|
|
151
|
-
2. **Store only necessary fields** extracted from PushDrop.
|
|
152
|
-
3. **Reject malformed or irrelevant outputs** early.
|
|
153
|
-
4. **Handle duplicate admissions** cleanly.
|
|
154
|
-
5. **Avoid heavy computation** inside the Topic Manager.
|
|
155
|
-
|
|
156
|
-
Example (Convo):
|
|
157
|
-
|
|
158
|
-
```ts
|
|
159
|
-
const decoded = PushDrop.decode(output.lockingScript)
|
|
160
|
-
const marker = Utils.toUTF8(fields[0])
|
|
161
|
-
const protocol = Utils.toUTF8(fields[1])
|
|
162
|
-
|
|
163
|
-
if (marker === 'convo' && protocol === 'tmconvo') {
|
|
164
|
-
admissibleOutputs.push(index)
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## 5. Data Retention and Cleanup
|
|
171
|
-
|
|
172
|
-
Overlays accumulate data continuously. Without retention logic, performance may degrade.
|
|
173
|
-
|
|
174
|
-
Common retention approaches:
|
|
175
|
-
|
|
176
|
-
* Delete old entries if your application doesn’t need them
|
|
177
|
-
* Archive long-lived data to another collection
|
|
178
|
-
* Rebuild indexes periodically
|
|
179
|
-
* Limit history for ephemeral overlays
|
|
180
|
-
|
|
181
|
-
Retention periods depend on the app:
|
|
182
|
-
|
|
183
|
-
* **Convo Messenger:** often stores all messages indefinitely
|
|
184
|
-
* **Task-based overlays:** may delete resolved items after 30–90 days
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## 6. Summary of Recommendations
|
|
189
|
-
|
|
190
|
-
| Area | Recommendation |
|
|
191
|
-
| --------------- | ---------------------------------------------------------------- |
|
|
192
|
-
| Required Fields | Always store `txid`, `outputIndex`, and protocol ID. |
|
|
193
|
-
| Metadata | Store only small, queryable fields (e.g., `threadId`, `sender`). |
|
|
194
|
-
| Large Data | Use UHRP references instead of storing raw data. |
|
|
195
|
-
| Queries | Use selective, indexed fields. |
|
|
196
|
-
| Pagination | Always include `skip` and `limit` in list endpoints. |
|
|
197
|
-
| Topic Manager | Validate and parse overlay-specific outputs only. |
|
|
198
|
-
| Retention | Prune or archive data as needed. |
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
Next file: [`02-query-performance.md`](./02-query-performance.md) — how to design and optimize overlay queries.
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
# Query Performance and Indexing for Overlays
|
|
2
|
-
|
|
3
|
-
[🏠 Home](../README.md) | [📚 API](../API.md) | [💡 Concepts](./README.md) | [📖 Examples](../examples/README.md) | [⚙️ Internal](../internal/README.md)
|
|
4
|
-
|
|
5
|
-
**Navigation:** [Overview](./00-overview.md) | [Best Practices](./01-best-practices.md) | [Query Performance](./02-query-performance.md) | [Database Monitoring](./03-database-monitoring.md) | [Pagination Example](./04-pagination-example.md) | [Recommendations Summary](./05-recommendations-summary.md)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
Efficient query design is critical for scalable overlay performance. Lookup Services often serve thousands of queries per day—fetching messages, reactions, and thread activity—so poor indexing or unbounded queries can quickly degrade performance.
|
|
12
|
-
|
|
13
|
-
This document explains how to design efficient queries for overlay Lookup Services, using real examples from Convo Messenger.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 1. Query Design Guidelines
|
|
18
|
-
|
|
19
|
-
### 1.1 Use Indexed Fields for All Queries
|
|
20
|
-
|
|
21
|
-
Indexes are critical for overlay scalability. Without them, MongoDB must perform full collection scans — a major performance bottleneck.
|
|
22
|
-
|
|
23
|
-
**Always index fields used in query filters**, such as:
|
|
24
|
-
|
|
25
|
-
* `threadId`
|
|
26
|
-
* `sender`
|
|
27
|
-
* `recipient`
|
|
28
|
-
* `createdAt`
|
|
29
|
-
|
|
30
|
-
Example:
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
db.convoMessages.createIndex({ threadId: 1 });
|
|
34
|
-
db.convoMessages.createIndex({ parentMessageId: 1 });
|
|
35
|
-
db.convoMessages.createIndex({ threadId: 1, createdAt: -1 });
|
|
36
|
-
db.convoReactions.createIndex({ threadId: 1 });
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
These fields match the filters used by:
|
|
40
|
-
|
|
41
|
-
* `listThreadMessages`
|
|
42
|
-
* `listReplies`
|
|
43
|
-
* `listThreadReactions`
|
|
44
|
-
* `countThreadMessages`
|
|
45
|
-
* `countReplies`
|
|
46
|
-
|
|
47
|
-
Rule:
|
|
48
|
-
Design indexes to match the exact fields your Lookup queries use.
|
|
49
|
-
|
|
50
|
-
### 1.2 Use Range Queries for Time-Based Lookups
|
|
51
|
-
|
|
52
|
-
If you need to filter messages by time, use
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
{ createdAt: { $gte: start, $lte: end } }
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Example pattern (recommended if your overlay uses time-range queries):
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
db.messages.find({
|
|
62
|
-
threadId,
|
|
63
|
-
createdAt: { $gte: startTime }
|
|
64
|
-
})
|
|
65
|
-
.sort({ createdAt: -1 })
|
|
66
|
-
.limit(50)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
This ensures MongoDB uses timestamp indexes efficiently.
|
|
70
|
-
|
|
71
|
-
### 1.3 Combine Pagination With Indexed Sorts
|
|
72
|
-
|
|
73
|
-
When paginating through sorted data, combine `skip` / `limit` with an indexed sort key.
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
async listThreadMessages(threadId, skip, limit) {
|
|
77
|
-
return await this.messages
|
|
78
|
-
.aggregate([
|
|
79
|
-
{ $match: { threadId } },
|
|
80
|
-
{ $sort: { createdAt: -1 } },
|
|
81
|
-
{ $skip: skip },
|
|
82
|
-
{ $limit: limit }
|
|
83
|
-
])
|
|
84
|
-
.toArray();
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Ensure the sort key (`createdAt`) matches an existing index to avoid in-memory sorting.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## 2. Common Anti-Patterns
|
|
93
|
-
|
|
94
|
-
| Problematic Pattern | Description | Recommended Fix |
|
|
95
|
-
| ---------------------------- | ------------------------------------------------------- | --------------------------------------------------- |
|
|
96
|
-
| **Unindexed Filters** | Querying on non-indexed fields causes full scans. | Add composite indexes on frequent filter fields. |
|
|
97
|
-
| **Regex or `$text` queries** | Regex searches prevent index usage. | Use prefix matches or precomputed lowercase fields. |
|
|
98
|
-
| **Large `$in` filters** | `$in` with many values increases memory and CPU load. | Batch requests or use intermediate caching. |
|
|
99
|
-
| **Unbounded queries** | Returning unbounded lists consumes memory. | Always apply limit. |
|
|
100
|
-
| **Sorting without an index** | Forces MongoDB to sort in memory. | Add compound index with your sort key. |
|
|
101
|
-
|
|
102
|
-
Examples to avoid:
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
// ❌ DO NOT DO THIS
|
|
106
|
-
db.messages.find({}).sort({ createdAt: -1 });
|
|
107
|
-
|
|
108
|
-
// ❌ Breaks index usage
|
|
109
|
-
db.messages.find({ sender: { $regex: '^03' } });
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## 3. MongoDB Indexing Best Practices
|
|
115
|
-
|
|
116
|
-
### 3.1 Use Compound Indexes
|
|
117
|
-
|
|
118
|
-
For overlays, most queries filter by a primary grouping field (such as threadId) and a secondary field (such as createdAt). Compound indexes drastically improve performance:
|
|
119
|
-
|
|
120
|
-
```js
|
|
121
|
-
// convoMessages collection
|
|
122
|
-
db.convoMessages.createIndex({ threadId: 1 });
|
|
123
|
-
db.convoMessages.createIndex({ parentMessageId: 1 });
|
|
124
|
-
db.convoMessages.createIndex({ threadId: 1, createdAt: -1 });
|
|
125
|
-
|
|
126
|
-
// convoReactions collection
|
|
127
|
-
db.convoReactions.createIndex({ threadId: 1 });
|
|
128
|
-
|
|
129
|
-
// convoThreads collection
|
|
130
|
-
db.convoThreads.createIndex({ threadId: 1 }, { unique: true });
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
These support efficient implmentations of:
|
|
134
|
-
|
|
135
|
-
**Messages in a thread**
|
|
136
|
-
```typescript
|
|
137
|
-
this.messages
|
|
138
|
-
.find({ threadId })
|
|
139
|
-
.sort({ createdAt: 1 });
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**Replies to a message**
|
|
143
|
-
```typescript
|
|
144
|
-
this.messages
|
|
145
|
-
.find({ parentMessageId })
|
|
146
|
-
.sort({ createdAt: 1 });
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**Latest messages across all threads**
|
|
150
|
-
Aggregation pipeline:
|
|
151
|
-
```typescript
|
|
152
|
-
[
|
|
153
|
-
{ $sort: { createdAt: -1 } },
|
|
154
|
-
{ $group: {
|
|
155
|
-
_id: "$threadId",
|
|
156
|
-
threadId: { $first: "$threadId" },
|
|
157
|
-
txid: { $first: "$txid" },
|
|
158
|
-
outputIndex: { $first: "$outputIndex" },
|
|
159
|
-
sender: { $first: "$sender" },
|
|
160
|
-
header: { $first: "$header" },
|
|
161
|
-
encryptedPayload: { $first: "$encryptedPayload" },
|
|
162
|
-
createdAt: { $first: "$createdAt" },
|
|
163
|
-
threadName: { $first: "$threadName" },
|
|
164
|
-
parentMessageId: { $first: "$parentMessageId" },
|
|
165
|
-
uniqueId: { $first: "$uniqueId" }
|
|
166
|
-
}},
|
|
167
|
-
{ $sort: { createdAt: -1 } },
|
|
168
|
-
{ $skip: skip },
|
|
169
|
-
{ $limit: limit }
|
|
170
|
-
]
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
All supported by the existing indexes.
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
### 3.2 Monitor Index Usage
|
|
178
|
-
|
|
179
|
-
Use MongoDB’s `explain()` command to verify index utilization:
|
|
180
|
-
|
|
181
|
-
Example (threads’ messages lookup):
|
|
182
|
-
```js
|
|
183
|
-
db.convoMessages
|
|
184
|
-
.find({ threadId: "abc123" })
|
|
185
|
-
.sort({ createdAt: -1 })
|
|
186
|
-
.explain("executionStats");
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Key metrics to monitor:
|
|
190
|
-
|
|
191
|
-
* `executionTimeMillis` - how long the query took
|
|
192
|
-
* `totalDocsExamined` - number of documents scanned
|
|
193
|
-
* `totalKeysExamined` - number of index entries scanned
|
|
194
|
-
|
|
195
|
-
**Goal:**
|
|
196
|
-
`totalDocsExamined` should be low (ideally equal to or close to limit) for all Convo queries.
|
|
197
|
-
|
|
198
|
-
### 3.3 Rebuild Indexes Periodically
|
|
199
|
-
|
|
200
|
-
Indexes can fragment over time, especially with:
|
|
201
|
-
|
|
202
|
-
* frequent inserts
|
|
203
|
-
* deletes (like messages deleted when spent)
|
|
204
|
-
* updates
|
|
205
|
-
|
|
206
|
-
Schedule index rebuilds during low-traffic periods:
|
|
207
|
-
```js
|
|
208
|
-
db.convoMessages.reIndex();
|
|
209
|
-
db.convoReactions.reIndex();
|
|
210
|
-
db.convoThreads.reIndex();
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## 4. Measuring Query Performance
|
|
216
|
-
|
|
217
|
-
### 4.1 Use Built-In Mongo Metrics
|
|
218
|
-
|
|
219
|
-
MongoDB includes a lightweight profiler that records slow queries.
|
|
220
|
-
This is the easiest way for overlay authors to discover inefficient lookups.
|
|
221
|
-
|
|
222
|
-
Enable profiling for any query taking longer than 100 ms:
|
|
223
|
-
|
|
224
|
-
```js
|
|
225
|
-
db.setProfilingLevel(1, { slowms: 100 });
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Slow query entries appear in the system.profile collection and can be inspected manually or exported to monitoring tools such as:
|
|
229
|
-
|
|
230
|
-
* Prometheus
|
|
231
|
-
* Grafana
|
|
232
|
-
* ELK / OpenSearch
|
|
233
|
-
* Custom dashboards
|
|
234
|
-
|
|
235
|
-
What to look for:
|
|
236
|
-
|
|
237
|
-
* High executionTimeMillis
|
|
238
|
-
* High totalDocsExamined (index not used)
|
|
239
|
-
* High totalKeysExamined (index scan too large)
|
|
240
|
-
|
|
241
|
-
Correctly indexed Convo queries should examine very small numbers of documents.
|
|
242
|
-
|
|
243
|
-
### 4.2 Add Overlay-Level Timing Logs
|
|
244
|
-
|
|
245
|
-
Your Lookup Service can log query timing directly at the overlay level.
|
|
246
|
-
This measures actual latency experienced by clients, not just database timings.
|
|
247
|
-
|
|
248
|
-
Example (Convo):
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
const start = performance.now();
|
|
252
|
-
const messages = await this.storage.listThreadMessages(
|
|
253
|
-
threadId,
|
|
254
|
-
skip,
|
|
255
|
-
limit
|
|
256
|
-
);
|
|
257
|
-
console.log(
|
|
258
|
-
`[ls_convo] listThreadMessages(${threadId}) took ${performance.now() - start} ms`
|
|
259
|
-
);
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
You can add similar timing logs to:
|
|
263
|
-
|
|
264
|
-
* listThreadReactions
|
|
265
|
-
* listReplies
|
|
266
|
-
* listLatestMessages
|
|
267
|
-
*countThreadMessages
|
|
268
|
-
* countReplies
|
|
269
|
-
|
|
270
|
-
This makes it easy to identify:
|
|
271
|
-
|
|
272
|
-
* Query patterns that degrade at scale
|
|
273
|
-
* Missed indexes
|
|
274
|
-
* Inefficient aggregation pipelines
|
|
275
|
-
|
|
276
|
-
Overlay-level logs are invaluable for practical debugging because they show the exact request→response timing seen by apps like Convo Messenger.
|
|
277
|
-
|
|
278
|
-
### 4.3 Performance Visualization Tooling (Proposed)
|
|
279
|
-
|
|
280
|
-
Although not required for overlay authors today, you can add your own simple visualizations.
|
|
281
|
-
|
|
282
|
-
Potential enhancements include:
|
|
283
|
-
|
|
284
|
-
* **Query Time Dashboard**
|
|
285
|
-
|
|
286
|
-
A small local dashboard showing average duration per lookup type (e.g., listThreadMessages, listReplies).
|
|
287
|
-
|
|
288
|
-
* **Slow Query Warnings**
|
|
289
|
-
|
|
290
|
-
Log a warning if any Lookup query exceeds a threshold (e.g., 200 ms).
|
|
291
|
-
|
|
292
|
-
* **Index Usage Reports**
|
|
293
|
-
|
|
294
|
-
A small script that uses explain("executionStats") on common queries and prints index efficiency.
|
|
295
|
-
|
|
296
|
-
These tools can help team-level debugging (e.g., Convo, Tempo, MetaMarket), but they are not a requirement for overlay authors and should not be confused with BSVA-level monitoring.
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
## 5. Practical Example: Convo Messenger
|
|
301
|
-
|
|
302
|
-
Convo Messenger provides a useful real-world example of how overlay query performance improves when Lookup queries are designed around indexed fields and predictable patterns.
|
|
303
|
-
|
|
304
|
-
Convo optimized its Lookup queries by:
|
|
305
|
-
|
|
306
|
-
Indexing fields used in queries, including:
|
|
307
|
-
|
|
308
|
-
- `threadId`
|
|
309
|
-
- `parentMessageId`
|
|
310
|
-
- `createdAt`
|
|
311
|
-
- compound index: `{ threadId: 1, createdAt: -1 }`
|
|
312
|
-
|
|
313
|
-
Using pagination everywhere, with sensible limits such as:
|
|
314
|
-
|
|
315
|
-
- `50` for message lists
|
|
316
|
-
- `100` for reaction lists
|
|
317
|
-
|
|
318
|
-
Ensuring query patterns always match existing indexes, including:
|
|
319
|
-
|
|
320
|
-
- filtering by `threadId`
|
|
321
|
-
- time ordering by `createdAt`
|
|
322
|
-
- grouping aggregation only after an indexed sort
|
|
323
|
-
- avoiding unbounded fetches (`find({})`)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
These design choices ensure Convo’s Lookup Service remains stable and scales predictably, even as message volume grows.
|
|
327
|
-
|
|
328
|
-
This example illustrates how overlays can maintain efficient performance without requiring complex systems—just well-designed indexes, selective filters, and consistent pagination.
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
## 6. Recommendations Summary
|
|
333
|
-
|
|
334
|
-
| Area | Recommendation |
|
|
335
|
-
| ------------ | ----------------------------------------------------------- |
|
|
336
|
-
| Query Design | Always use indexed filters; avoid regex and `$in` scans. |
|
|
337
|
-
| Indexing | Add indexes that match your overlay’s query filters (e.g., `{ threadId, createdAt }`). |
|
|
338
|
-
| Pagination | Combine with indexed sorts to avoid in-memory sorting. |
|
|
339
|
-
| Measurement | Use MongoDB profiler and `explain('executionStats')` to detect slow queries. |
|
|
340
|
-
| Tooling | Explore adding an Overlay Query Monitor to Overlay Express. |
|
|
341
|
-
| Logging | Add timing logs in Lookup Services to measure real client-facing latency |
|
|
342
|
-
|
|
343
|
-
---
|
|
344
|
-
|
|
345
|
-
Next file: [`03-database-monitoring.md`](./03-database-monitoring.md) — focuses on database health, read-only access for debugging, and workflows for CARS admins.
|