@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.
Files changed (155) hide show
  1. package/LICENSE.txt +57 -27
  2. package/README.md +102 -266
  3. package/dist/cjs/mod.d.ts +15 -0
  4. package/dist/cjs/mod.d.ts.map +1 -0
  5. package/dist/cjs/mod.js +4 -1
  6. package/dist/cjs/mod.js.map +1 -1
  7. package/dist/cjs/package.json +46 -18
  8. package/dist/cjs/src/Advertisement.d.ts +9 -0
  9. package/dist/cjs/src/Advertisement.d.ts.map +1 -0
  10. package/dist/cjs/src/Advertiser.d.ts +36 -0
  11. package/dist/cjs/src/Advertiser.d.ts.map +1 -0
  12. package/dist/cjs/src/BASM.d.ts +110 -0
  13. package/dist/cjs/src/BASM.d.ts.map +1 -0
  14. package/dist/cjs/src/BASM.js.map +1 -1
  15. package/dist/cjs/src/BASMRemote.d.ts +14 -0
  16. package/dist/cjs/src/BASMRemote.d.ts.map +1 -0
  17. package/dist/cjs/src/BASMRemote.js.map +1 -1
  18. package/dist/cjs/src/Engine.d.ts +377 -0
  19. package/dist/cjs/src/Engine.d.ts.map +1 -0
  20. package/dist/cjs/src/Engine.js +430 -436
  21. package/dist/cjs/src/Engine.js.map +1 -1
  22. package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +24 -0
  23. package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -0
  24. package/dist/cjs/src/GASP/OverlayGASPRemote.js +7 -10
  25. package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
  26. package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +103 -0
  27. package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -0
  28. package/dist/cjs/src/GASP/OverlayGASPStorage.js +34 -31
  29. package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
  30. package/dist/cjs/src/LookupFormula.d.ts +29 -0
  31. package/dist/cjs/src/LookupFormula.d.ts.map +1 -0
  32. package/dist/cjs/src/LookupService.d.ts +85 -0
  33. package/dist/cjs/src/LookupService.d.ts.map +1 -0
  34. package/dist/cjs/src/Output.d.ts +32 -0
  35. package/dist/cjs/src/Output.d.ts.map +1 -0
  36. package/dist/cjs/src/SafeLog.d.ts +14 -0
  37. package/dist/cjs/src/SafeLog.d.ts.map +1 -0
  38. package/dist/cjs/src/SafeLog.js +44 -0
  39. package/dist/cjs/src/SafeLog.js.map +1 -0
  40. package/{src/SyncConfiguration.ts → dist/cjs/src/SyncConfiguration.d.ts} +2 -1
  41. package/dist/cjs/src/SyncConfiguration.d.ts.map +1 -0
  42. package/dist/cjs/src/TopicManager.d.ts +35 -0
  43. package/dist/cjs/src/TopicManager.d.ts.map +1 -0
  44. package/dist/cjs/src/storage/Storage.d.ts +237 -0
  45. package/dist/cjs/src/storage/Storage.d.ts.map +1 -0
  46. package/dist/cjs/src/storage/knex/KnexStorage.d.ts +69 -0
  47. package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -0
  48. package/dist/cjs/src/storage/knex/KnexStorage.js +7 -10
  49. package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
  50. package/dist/cjs/src/storage/knex/all-migrations.d.ts +11 -0
  51. package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -0
  52. package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
  53. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
  54. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
  55. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
  56. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
  57. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
  58. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
  59. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts +4 -0
  60. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -0
  61. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js +36 -12
  62. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
  63. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts +4 -0
  64. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -0
  65. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
  66. package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts +4 -0
  67. package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -0
  68. package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
  69. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
  70. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
  71. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -1
  72. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
  73. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
  74. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -1
  75. package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts +9 -0
  76. package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts.map +1 -0
  77. package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.js.map +1 -1
  78. package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
  79. package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
  80. package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -1
  81. package/dist/esm/mod.js +2 -1
  82. package/dist/esm/mod.js.map +1 -1
  83. package/dist/esm/src/BASMRemote.js.map +1 -1
  84. package/dist/esm/src/Engine.js +408 -414
  85. package/dist/esm/src/Engine.js.map +1 -1
  86. package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -2
  87. package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
  88. package/dist/esm/src/GASP/OverlayGASPStorage.js +23 -19
  89. package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
  90. package/dist/esm/src/SafeLog.js +40 -0
  91. package/dist/esm/src/SafeLog.js.map +1 -0
  92. package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
  93. package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js +36 -12
  94. package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
  95. package/dist/types/mod.d.ts +5 -4
  96. package/dist/types/mod.d.ts.map +1 -1
  97. package/dist/types/src/BASMRemote.d.ts.map +1 -1
  98. package/dist/types/src/Engine.d.ts +15 -0
  99. package/dist/types/src/Engine.d.ts.map +1 -1
  100. package/dist/types/src/GASP/OverlayGASPRemote.d.ts +2 -2
  101. package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
  102. package/dist/types/src/GASP/OverlayGASPStorage.d.ts +3 -1
  103. package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
  104. package/dist/types/src/SafeLog.d.ts +14 -0
  105. package/dist/types/src/SafeLog.d.ts.map +1 -0
  106. package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
  107. package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
  108. package/dist/types/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -1
  109. package/package.json +87 -30
  110. package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
  111. package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
  112. package/dist/types/tsconfig.types.tsbuildinfo +0 -1
  113. package/docs/API.md +0 -944
  114. package/docs/BRC-136-BASM.md +0 -327
  115. package/docs/README.md +0 -16
  116. package/docs/Synchronization.md +0 -212
  117. package/docs/concepts/00-overview.md +0 -85
  118. package/docs/concepts/01-best-practices.md +0 -202
  119. package/docs/concepts/02-query-performance.md +0 -345
  120. package/docs/concepts/03-database-monitoring.md +0 -211
  121. package/docs/concepts/04-pagination-example.md +0 -186
  122. package/docs/concepts/05-recommendations-summary.md +0 -158
  123. package/docs/concepts/README.md +0 -20
  124. package/docs/examples/README.md +0 -15
  125. package/docs/examples/gs-wip.md +0 -111
  126. package/docs/internal/README.md +0 -11
  127. package/mod.ts +0 -40
  128. package/src/Advertisement.ts +0 -8
  129. package/src/Advertiser.ts +0 -41
  130. package/src/BASM.ts +0 -208
  131. package/src/BASMRemote.ts +0 -54
  132. package/src/Engine.ts +0 -2199
  133. package/src/GASP/OverlayGASPRemote.ts +0 -108
  134. package/src/GASP/OverlayGASPStorage.ts +0 -383
  135. package/src/LookupFormula.ts +0 -31
  136. package/src/LookupService.ts +0 -136
  137. package/src/Output.ts +0 -31
  138. package/src/TopicManager.ts +0 -40
  139. package/src/__tests/BASM.test.ts +0 -44
  140. package/src/__tests/BASMChain.test.ts +0 -290
  141. package/src/__tests/Engine.test.ts +0 -1345
  142. package/src/__tests/OverlayGASPRemote.test.ts +0 -135
  143. package/src/__tests/OverlayGASPStorage.test.ts +0 -171
  144. package/src/storage/Storage.ts +0 -260
  145. package/src/storage/knex/KnexStorage.ts +0 -651
  146. package/src/storage/knex/all-migrations.ts +0 -32
  147. package/src/storage/knex/migrations/2024-05-18-001-initial.ts +0 -30
  148. package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +0 -15
  149. package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +0 -40
  150. package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +0 -30
  151. package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +0 -42
  152. package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +0 -46
  153. package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +0 -24
  154. package/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.ts +0 -18
  155. 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.