@atproto/bsky 0.0.81 → 0.0.82

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 (44) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/data-plane/client.d.ts.map +1 -1
  3. package/dist/data-plane/client.js +2 -1
  4. package/dist/data-plane/client.js.map +1 -1
  5. package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.d.ts +4 -0
  6. package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.d.ts.map +1 -0
  7. package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.js +26 -0
  8. package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.js.map +1 -0
  9. package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
  10. package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
  11. package/dist/data-plane/server/db/migrations/index.js +2 -1
  12. package/dist/data-plane/server/db/migrations/index.js.map +1 -1
  13. package/dist/data-plane/server/db/tables/actor-sync.d.ts +0 -3
  14. package/dist/data-plane/server/db/tables/actor-sync.d.ts.map +1 -1
  15. package/dist/data-plane/server/db/tables/actor-sync.js.map +1 -1
  16. package/dist/data-plane/server/indexing/index.d.ts +2 -7
  17. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  18. package/dist/data-plane/server/indexing/index.js +4 -21
  19. package/dist/data-plane/server/indexing/index.js.map +1 -1
  20. package/dist/data-plane/server/subscription.d.ts +26 -0
  21. package/dist/data-plane/server/subscription.d.ts.map +1 -0
  22. package/dist/data-plane/server/subscription.js +115 -0
  23. package/dist/data-plane/server/subscription.js.map +1 -0
  24. package/package.json +4 -3
  25. package/src/data-plane/client.ts +4 -1
  26. package/src/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.ts +23 -0
  27. package/src/data-plane/server/db/migrations/index.ts +1 -0
  28. package/src/data-plane/server/db/tables/actor-sync.ts +0 -3
  29. package/src/data-plane/server/indexing/index.ts +4 -25
  30. package/src/data-plane/server/subscription.ts +104 -0
  31. package/tests/data-plane/indexing.test.ts +1 -1
  32. package/tests/data-plane/{subscription/repo.test.ts → subscription.test.ts} +4 -9
  33. package/tests/views/actor-search.test.ts +1 -1
  34. package/dist/data-plane/server/subscription/index.d.ts +0 -33
  35. package/dist/data-plane/server/subscription/index.d.ts.map +0 -1
  36. package/dist/data-plane/server/subscription/index.js +0 -341
  37. package/dist/data-plane/server/subscription/index.js.map +0 -1
  38. package/dist/data-plane/server/subscription/util.d.ts +0 -65
  39. package/dist/data-plane/server/subscription/util.d.ts.map +0 -1
  40. package/dist/data-plane/server/subscription/util.js +0 -215
  41. package/dist/data-plane/server/subscription/util.js.map +0 -1
  42. package/src/data-plane/server/subscription/index.ts +0 -352
  43. package/src/data-plane/server/subscription/util.ts +0 -156
  44. package/tests/data-plane/subscription/util.test.ts +0 -185
@@ -1,215 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.strToInt = exports.jitter = exports.loggableMessage = exports.PerfectMap = exports.ConsecutiveItem = exports.ConsecutiveList = exports.LatestQueue = exports.PartitionedQueue = void 0;
30
- const node_assert_1 = __importDefault(require("node:assert"));
31
- const p_queue_1 = __importDefault(require("p-queue"));
32
- const message = __importStar(require("../../../lexicon/types/com/atproto/sync/subscribeRepos"));
33
- // A queue with arbitrarily many partitions, each processing work sequentially.
34
- // Partitions are created lazily and taken out of memory when they go idle.
35
- class PartitionedQueue {
36
- constructor(opts) {
37
- Object.defineProperty(this, "main", {
38
- enumerable: true,
39
- configurable: true,
40
- writable: true,
41
- value: void 0
42
- });
43
- Object.defineProperty(this, "partitions", {
44
- enumerable: true,
45
- configurable: true,
46
- writable: true,
47
- value: new Map()
48
- });
49
- this.main = new p_queue_1.default({ concurrency: opts.concurrency });
50
- }
51
- async add(partitionId, task) {
52
- if (this.main.isPaused)
53
- return;
54
- return this.main.add(() => {
55
- return this.getPartition(partitionId).add(task);
56
- });
57
- }
58
- async destroy() {
59
- this.main.pause();
60
- this.main.clear();
61
- this.partitions.forEach((p) => p.clear());
62
- await this.main.onIdle(); // All in-flight work completes
63
- }
64
- getPartition(partitionId) {
65
- let partition = this.partitions.get(partitionId);
66
- if (!partition) {
67
- partition = new p_queue_1.default({ concurrency: 1 });
68
- partition.once('idle', () => this.partitions.delete(partitionId));
69
- this.partitions.set(partitionId, partition);
70
- }
71
- return partition;
72
- }
73
- }
74
- exports.PartitionedQueue = PartitionedQueue;
75
- class LatestQueue {
76
- constructor() {
77
- Object.defineProperty(this, "queue", {
78
- enumerable: true,
79
- configurable: true,
80
- writable: true,
81
- value: new p_queue_1.default({ concurrency: 1 })
82
- });
83
- }
84
- async add(task) {
85
- if (this.queue.isPaused)
86
- return;
87
- this.queue.clear(); // Only queue the latest task, invalidate any previous ones
88
- return this.queue.add(task);
89
- }
90
- async destroy() {
91
- this.queue.pause();
92
- this.queue.clear();
93
- await this.queue.onIdle(); // All in-flight work completes
94
- }
95
- }
96
- exports.LatestQueue = LatestQueue;
97
- /**
98
- * Add items to a list, and mark those items as
99
- * completed. Upon item completion, get list of consecutive
100
- * items completed at the head of the list. Example:
101
- *
102
- * const consecutive = new ConsecutiveList<number>()
103
- * const item1 = consecutive.push(1)
104
- * const item2 = consecutive.push(2)
105
- * const item3 = consecutive.push(3)
106
- * item2.complete() // []
107
- * item1.complete() // [1, 2]
108
- * item3.complete() // [3]
109
- *
110
- */
111
- class ConsecutiveList {
112
- constructor() {
113
- Object.defineProperty(this, "list", {
114
- enumerable: true,
115
- configurable: true,
116
- writable: true,
117
- value: []
118
- });
119
- }
120
- push(value) {
121
- const item = new ConsecutiveItem(this, value);
122
- this.list.push(item);
123
- return item;
124
- }
125
- complete() {
126
- let i = 0;
127
- while (this.list[i]?.isComplete) {
128
- i += 1;
129
- }
130
- return this.list.splice(0, i).map((item) => item.value);
131
- }
132
- }
133
- exports.ConsecutiveList = ConsecutiveList;
134
- class ConsecutiveItem {
135
- constructor(consecutive, value) {
136
- Object.defineProperty(this, "consecutive", {
137
- enumerable: true,
138
- configurable: true,
139
- writable: true,
140
- value: consecutive
141
- });
142
- Object.defineProperty(this, "value", {
143
- enumerable: true,
144
- configurable: true,
145
- writable: true,
146
- value: value
147
- });
148
- Object.defineProperty(this, "isComplete", {
149
- enumerable: true,
150
- configurable: true,
151
- writable: true,
152
- value: false
153
- });
154
- }
155
- complete() {
156
- this.isComplete = true;
157
- return this.consecutive.complete();
158
- }
159
- }
160
- exports.ConsecutiveItem = ConsecutiveItem;
161
- class PerfectMap extends Map {
162
- get(key) {
163
- const val = super.get(key);
164
- (0, node_assert_1.default)(val !== undefined, `Key not found in PerfectMap: ${key}`);
165
- return val;
166
- }
167
- }
168
- exports.PerfectMap = PerfectMap;
169
- function loggableMessage(msg) {
170
- if (message.isCommit(msg)) {
171
- const { seq, rebase, prev, repo, commit, time, tooBig, blobs } = msg;
172
- return {
173
- $type: msg.$type,
174
- seq,
175
- rebase,
176
- prev: prev?.toString(),
177
- repo,
178
- commit: commit.toString(),
179
- time,
180
- tooBig,
181
- hasBlobs: blobs.length > 0,
182
- };
183
- }
184
- else if (message.isHandle(msg)) {
185
- return msg;
186
- }
187
- else if (message.isIdentity(msg)) {
188
- return msg;
189
- }
190
- else if (message.isAccount(msg)) {
191
- return msg;
192
- }
193
- else if (message.isMigrate(msg)) {
194
- return msg;
195
- }
196
- else if (message.isTombstone(msg)) {
197
- return msg;
198
- }
199
- else if (message.isInfo(msg)) {
200
- return msg;
201
- }
202
- return msg;
203
- }
204
- exports.loggableMessage = loggableMessage;
205
- function jitter(maxMs) {
206
- return Math.round((Math.random() - 0.5) * maxMs * 2);
207
- }
208
- exports.jitter = jitter;
209
- function strToInt(str) {
210
- const int = parseInt(str, 10);
211
- (0, node_assert_1.default)(!isNaN(int), 'string could not be parsed to an integer');
212
- return int;
213
- }
214
- exports.strToInt = strToInt;
215
- //# sourceMappingURL=util.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../../../../src/data-plane/server/subscription/util.ts"],"names":[],"mappings":"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"}
@@ -1,352 +0,0 @@
1
- import assert from 'node:assert'
2
- import { CID } from 'multiformats/cid'
3
- import { AtUri } from '@atproto/syntax'
4
- import { Subscription } from '@atproto/xrpc-server'
5
- import { cborDecode, handleAllSettledErrors } from '@atproto/common'
6
- import { ValidationError } from '@atproto/lexicon'
7
- import { IdResolver } from '@atproto/identity'
8
- import {
9
- WriteOpAction,
10
- readCarWithRoot,
11
- cborToLexRecord,
12
- def,
13
- Commit,
14
- } from '@atproto/repo'
15
- import { ids, lexicons } from '../../../lexicon/lexicons'
16
- import { OutputSchema as Message } from '../../../lexicon/types/com/atproto/sync/subscribeRepos'
17
- import * as message from '../../../lexicon/types/com/atproto/sync/subscribeRepos'
18
- import { subLogger as log } from '../../../logger'
19
- import { IndexingService } from '../indexing'
20
- import { Database } from '../db'
21
- import {
22
- ConsecutiveItem,
23
- ConsecutiveList,
24
- PartitionedQueue,
25
- ProcessableMessage,
26
- loggableMessage,
27
- } from './util'
28
- import { BackgroundQueue } from '../background'
29
-
30
- export class RepoSubscription {
31
- ac = new AbortController()
32
- running: Promise<void> | undefined
33
- cursor = 0
34
- seenSeq: number | null = null
35
- repoQueue = new PartitionedQueue({ concurrency: Infinity })
36
- consecutive = new ConsecutiveList<number>()
37
- background: BackgroundQueue
38
- indexingSvc: IndexingService
39
-
40
- constructor(
41
- private opts: {
42
- service: string
43
- db: Database
44
- idResolver: IdResolver
45
- background: BackgroundQueue
46
- },
47
- ) {
48
- this.background = new BackgroundQueue(this.opts.db)
49
- this.indexingSvc = new IndexingService(
50
- this.opts.db,
51
- this.opts.idResolver,
52
- this.background,
53
- )
54
- }
55
-
56
- run() {
57
- if (this.running) return
58
- this.ac = new AbortController()
59
- this.repoQueue = new PartitionedQueue({ concurrency: Infinity })
60
- this.consecutive = new ConsecutiveList<number>()
61
- this.running = this.process()
62
- .catch((err) => {
63
- if (err.name !== 'AbortError') {
64
- // allow this to cause an unhandled rejection, let deployment handle the crash.
65
- log.error({ err }, 'subscription crashed')
66
- throw err
67
- }
68
- })
69
- .finally(() => (this.running = undefined))
70
- }
71
-
72
- private async process() {
73
- const sub = this.getSubscription()
74
- for await (const msg of sub) {
75
- const details = getMessageDetails(msg)
76
- if ('info' in details) {
77
- // These messages are not sequenced, we just log them and carry on
78
- log.warn(
79
- { provider: this.opts.service, message: loggableMessage(msg) },
80
- `sub ${details.info ? 'info' : 'unknown'} message`,
81
- )
82
- continue
83
- }
84
- const item = this.consecutive.push(details.seq)
85
- this.repoQueue.add(details.repo, async () => {
86
- await this.handleMessage(item, details)
87
- })
88
- this.seenSeq = details.seq
89
- await this.repoQueue.main.onEmpty() // backpressure
90
- }
91
- }
92
-
93
- private async handleMessage(
94
- item: ConsecutiveItem<number>,
95
- envelope: Envelope,
96
- ) {
97
- const msg = envelope.message
98
- try {
99
- if (message.isCommit(msg)) {
100
- await this.handleCommit(msg)
101
- } else if (message.isHandle(msg)) {
102
- await this.handleUpdateHandle(msg)
103
- } else if (message.isIdentity(msg)) {
104
- await this.handleIdentityEvt(msg)
105
- } else if (message.isAccount(msg)) {
106
- await this.handleAccountEvt(msg)
107
- } else if (message.isTombstone(msg)) {
108
- // Ignore tombstones
109
- } else if (message.isMigrate(msg)) {
110
- // Ignore migrations
111
- } else {
112
- const exhaustiveCheck: never = msg
113
- throw new Error(`Unhandled message type: ${exhaustiveCheck['$type']}`)
114
- }
115
- } catch (err) {
116
- // We log messages we can't process and move on:
117
- // otherwise the cursor would get stuck on a poison message.
118
- log.error(
119
- { err, message: loggableMessage(msg) },
120
- 'indexer message processing error',
121
- )
122
- } finally {
123
- const latest = item.complete().at(-1)
124
- if (latest !== undefined) {
125
- this.cursor = latest
126
- }
127
- }
128
- }
129
-
130
- private async handleCommit(msg: message.Commit) {
131
- const indexRecords = async () => {
132
- const { root, rootCid, ops } = await getOps(msg)
133
- if (msg.tooBig) {
134
- await this.indexingSvc.indexRepo(msg.repo, rootCid.toString())
135
- await this.indexingSvc.setCommitLastSeen(root, msg)
136
- return
137
- }
138
- if (msg.rebase) {
139
- const needsReindex =
140
- await this.indexingSvc.checkCommitNeedsIndexing(root)
141
- if (needsReindex) {
142
- await this.indexingSvc.indexRepo(msg.repo, rootCid.toString())
143
- }
144
- await this.indexingSvc.setCommitLastSeen(root, msg)
145
- return
146
- }
147
- for (const op of ops) {
148
- if (op.action === WriteOpAction.Delete) {
149
- await this.indexingSvc.deleteRecord(op.uri)
150
- } else {
151
- try {
152
- await this.indexingSvc.indexRecord(
153
- op.uri,
154
- op.cid,
155
- op.record,
156
- op.action, // create or update
157
- msg.time,
158
- )
159
- } catch (err) {
160
- if (err instanceof ValidationError) {
161
- log.warn(
162
- {
163
- did: msg.repo,
164
- commit: msg.commit.toString(),
165
- uri: op.uri.toString(),
166
- cid: op.cid.toString(),
167
- },
168
- 'skipping indexing of invalid record',
169
- )
170
- } else {
171
- log.error(
172
- {
173
- err,
174
- did: msg.repo,
175
- commit: msg.commit.toString(),
176
- uri: op.uri.toString(),
177
- cid: op.cid.toString(),
178
- },
179
- 'skipping indexing due to error processing record',
180
- )
181
- }
182
- }
183
- }
184
- }
185
- await this.indexingSvc.setCommitLastSeen(root, msg)
186
- }
187
- const results = await Promise.allSettled([
188
- indexRecords(),
189
- this.indexingSvc.indexHandle(msg.repo, msg.time),
190
- ])
191
- handleAllSettledErrors(results)
192
- }
193
-
194
- private async handleUpdateHandle(msg: message.Handle) {
195
- await this.indexingSvc.indexHandle(msg.did, msg.time, true)
196
- }
197
-
198
- private async handleIdentityEvt(msg: message.Identity) {
199
- await this.indexingSvc.indexHandle(msg.did, msg.time, true)
200
- }
201
-
202
- private async handleAccountEvt(msg: message.Account) {
203
- if (msg.active === false && msg.status === 'deleted') {
204
- await this.indexingSvc.deleteActor(msg.did)
205
- } else {
206
- await this.indexingSvc.updateActorStatus(msg.did, msg.active, msg.status)
207
- }
208
- }
209
-
210
- private getSubscription() {
211
- return new Subscription({
212
- service: this.opts.service,
213
- method: ids.ComAtprotoSyncSubscribeRepos,
214
- signal: this.ac.signal,
215
- getParams: async () => {
216
- return { cursor: this.cursor }
217
- },
218
- onReconnectError: (err, reconnects, initial) => {
219
- log.warn({ err, reconnects, initial }, 'sub reconnect')
220
- },
221
- validate: (value) => {
222
- try {
223
- return lexicons.assertValidXrpcMessage<Message>(
224
- ids.ComAtprotoSyncSubscribeRepos,
225
- value,
226
- )
227
- } catch (err) {
228
- log.warn(
229
- {
230
- err,
231
- seq: ifNumber(value?.['seq']),
232
- repo: ifString(value?.['repo']),
233
- commit: ifString(value?.['commit']?.toString()),
234
- time: ifString(value?.['time']),
235
- provider: this.opts.service,
236
- },
237
- 'ingester sub skipped invalid message',
238
- )
239
- }
240
- },
241
- })
242
- }
243
-
244
- async destroy() {
245
- this.ac.abort()
246
- await this.running
247
- await this.repoQueue.destroy()
248
- await this.background.processAll()
249
- }
250
- }
251
-
252
- type Envelope = {
253
- repo: string
254
- message: ProcessableMessage
255
- }
256
-
257
- function ifString(val: unknown): string | undefined {
258
- return typeof val === 'string' ? val : undefined
259
- }
260
-
261
- function ifNumber(val: unknown): number | undefined {
262
- return typeof val === 'number' ? val : undefined
263
- }
264
-
265
- function getMessageDetails(msg: Message):
266
- | { info: message.Info | null }
267
- | {
268
- seq: number
269
- repo: string
270
- message: ProcessableMessage
271
- } {
272
- if (message.isCommit(msg)) {
273
- return { seq: msg.seq, repo: msg.repo, message: msg }
274
- } else if (message.isHandle(msg)) {
275
- return { seq: msg.seq, repo: msg.did, message: msg }
276
- } else if (message.isIdentity(msg)) {
277
- return { seq: msg.seq, repo: msg.did, message: msg }
278
- } else if (message.isAccount(msg)) {
279
- return { seq: msg.seq, repo: msg.did, message: msg }
280
- } else if (message.isMigrate(msg)) {
281
- return { seq: msg.seq, repo: msg.did, message: msg }
282
- } else if (message.isTombstone(msg)) {
283
- return { seq: msg.seq, repo: msg.did, message: msg }
284
- } else if (message.isInfo(msg)) {
285
- return { info: msg }
286
- }
287
- return { info: null }
288
- }
289
-
290
- async function getOps(
291
- msg: message.Commit,
292
- ): Promise<{ root: Commit; rootCid: CID; ops: PreparedWrite[] }> {
293
- const car = await readCarWithRoot(msg.blocks as Uint8Array)
294
- const rootBytes = car.blocks.get(car.root)
295
- assert(rootBytes, 'Missing commit block in car slice')
296
-
297
- const root = def.commit.schema.parse(cborDecode(rootBytes))
298
- const ops: PreparedWrite[] = msg.ops.map((op) => {
299
- const [collection, rkey] = op.path.split('/')
300
- assert(collection && rkey)
301
- if (
302
- op.action === WriteOpAction.Create ||
303
- op.action === WriteOpAction.Update
304
- ) {
305
- assert(op.cid)
306
- const record = car.blocks.get(op.cid)
307
- assert(record)
308
- return {
309
- action:
310
- op.action === WriteOpAction.Create
311
- ? WriteOpAction.Create
312
- : WriteOpAction.Update,
313
- cid: op.cid,
314
- record: cborToLexRecord(record),
315
- blobs: [],
316
- uri: AtUri.make(msg.repo, collection, rkey),
317
- }
318
- } else if (op.action === WriteOpAction.Delete) {
319
- return {
320
- action: WriteOpAction.Delete,
321
- uri: AtUri.make(msg.repo, collection, rkey),
322
- }
323
- } else {
324
- throw new Error(`Unknown repo op action: ${op.action}`)
325
- }
326
- })
327
-
328
- return { root, rootCid: car.root, ops }
329
- }
330
-
331
- type PreparedCreate = {
332
- action: WriteOpAction.Create
333
- uri: AtUri
334
- cid: CID
335
- record: Record<string, unknown>
336
- blobs: CID[] // differs from similar type in pds
337
- }
338
-
339
- type PreparedUpdate = {
340
- action: WriteOpAction.Update
341
- uri: AtUri
342
- cid: CID
343
- record: Record<string, unknown>
344
- blobs: CID[] // differs from similar type in pds
345
- }
346
-
347
- type PreparedDelete = {
348
- action: WriteOpAction.Delete
349
- uri: AtUri
350
- }
351
-
352
- type PreparedWrite = PreparedCreate | PreparedUpdate | PreparedDelete