@haathie/pgmb 0.2.9 → 0.2.11

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.
@@ -1,98 +0,0 @@
1
- import assert from 'assert'
2
-
3
- type AAResult<T> = IteratorResult<T>
4
-
5
- export class AbortableAsyncIterator<T> implements AsyncIterableIterator<T> {
6
- readonly signal: AbortSignal
7
- readonly onEnd: () => void
8
-
9
- ended = false
10
-
11
- #resolve: (() => void) | undefined
12
- #reject: ((reason?: unknown) => void) | undefined
13
- #queue: T[] = []
14
- #locked = false
15
-
16
- constructor(signal: AbortSignal, onEnd: () => void = () => {}) {
17
- this.signal = signal
18
- this.onEnd = onEnd
19
- signal.addEventListener('abort', this.#onAbort)
20
- }
21
-
22
- async next(): Promise<AAResult<T>> {
23
- assert(!this.ended, 'Iterator has already been completed')
24
- assert(!this.#locked, 'Concurrent calls to next() are not allowed')
25
-
26
- let nextItem = this.#queue.shift()
27
- if(nextItem) {
28
- return { value: nextItem, done: false }
29
- }
30
-
31
- this.#locked = true
32
- try {
33
- await this.#setupNextPromise()
34
- } finally {
35
- this.#locked = false
36
- }
37
-
38
- nextItem = this.#queue.shift()
39
- if(nextItem) {
40
- return { value: nextItem, done: false }
41
- }
42
-
43
- return { value: undefined, done: true }
44
- }
45
-
46
- enqueue(value: T) {
47
- assert(!this.ended, 'Iterator has already been completed')
48
- this.#queue.push(value)
49
- this.#resolve?.()
50
- }
51
-
52
- throw(reason?: unknown): Promise<AAResult<T>> {
53
- this.signal.throwIfAborted()
54
- this.#reject?.(reason)
55
- this.#end()
56
- return Promise.resolve({ done: true, value: undefined })
57
- }
58
-
59
- return(value?: any): Promise<AAResult<T>> {
60
- this.#resolve?.()
61
- this.#end()
62
- return Promise.resolve({ done: true, value })
63
- }
64
-
65
- #setupNextPromise() {
66
- return new Promise<void>((resolve, reject) => {
67
- this.#resolve = () => {
68
- resolve()
69
- this.#cleanupTask()
70
- }
71
-
72
- this.#reject = err => {
73
- reject(err)
74
- this.#cleanupTask()
75
- }
76
- })
77
- }
78
-
79
- #cleanupTask() {
80
- this.#resolve = undefined
81
- this.#reject = undefined
82
- }
83
-
84
- #onAbort = (reason: any) => {
85
- this.#reject?.(reason)
86
- this.#end()
87
- this.ended = true
88
- }
89
-
90
- #end() {
91
- this.signal.removeEventListener('abort', this.#onAbort)
92
- this.onEnd()
93
- }
94
-
95
- [Symbol.asyncIterator]() {
96
- return this
97
- }
98
- }
package/src/batcher.ts DELETED
@@ -1,90 +0,0 @@
1
- import type { IEventData, PGMBEventBatcherOpts } from './types.ts'
2
-
3
- type Batch<T> = {
4
- messages: T[]
5
- }
6
-
7
- export class PGMBEventBatcher<T extends IEventData> {
8
-
9
- #publish: PGMBEventBatcherOpts<T>['publish']
10
- #flushIntervalMs: number | undefined
11
- #maxBatchSize: number
12
- #currentBatch: Batch<T> = { messages: [] }
13
- #flushTimeout: NodeJS.Timeout | undefined
14
- #flushTask: Promise<void> | undefined
15
- #logger: PGMBEventBatcherOpts<T>['logger']
16
- #shouldLog?: PGMBEventBatcherOpts<T>['shouldLog']
17
- #batch = 0
18
-
19
- constructor({
20
- shouldLog,
21
- publish,
22
- flushIntervalMs,
23
- maxBatchSize = 2500,
24
- logger
25
- }: PGMBEventBatcherOpts<T>) {
26
- this.#publish = publish
27
- this.#flushIntervalMs = flushIntervalMs
28
- this.#maxBatchSize = maxBatchSize
29
- this.#logger = logger
30
- this.#shouldLog = shouldLog
31
- }
32
-
33
- async end() {
34
- clearTimeout(this.#flushTimeout)
35
- await this.#flushTask
36
- await this.flush()
37
- }
38
-
39
- /**
40
- * Enqueue a message to be published, will be flushed to the database
41
- * when flush() is called (either manually or via interval)
42
- */
43
- enqueue(msg: T) {
44
- this.#currentBatch.messages.push(msg)
45
- if(this.#currentBatch.messages.length >= this.#maxBatchSize) {
46
- this.flush()
47
- return
48
- }
49
-
50
- if(this.#flushTimeout || !this.#flushIntervalMs) {
51
- return
52
- }
53
-
54
- this.#flushTimeout = setTimeout(() => this.flush(), this.#flushIntervalMs)
55
- }
56
-
57
- async flush() {
58
- if(!this.#currentBatch.messages.length) {
59
- return
60
- }
61
-
62
- const batch = this.#currentBatch
63
- this.#currentBatch = { messages: [] }
64
- clearTimeout(this.#flushTimeout)
65
- this.#flushTimeout = undefined
66
-
67
- await this.#flushTask
68
-
69
- this.#flushTask = this.#publishBatch(batch)
70
- return this.#flushTask
71
- }
72
-
73
- async #publishBatch({ messages }: Batch<T>) {
74
- const batch = ++this.#batch
75
- try {
76
- const ids = await this.#publish(...messages)
77
- for(const [i, { id }] of ids.entries()) {
78
- if(this.#shouldLog && !this.#shouldLog(messages[i])) {
79
- continue
80
- }
81
-
82
- this.#logger
83
- ?.info({ batch, id, message: messages[i] }, 'published message')
84
- }
85
- } catch(err) {
86
- this.#logger
87
- ?.error({ batch, err, msgs: messages }, 'failed to publish messages')
88
- }
89
- }
90
- }