@@ -6,12 +6,13 @@ import { v4 as uuid } from 'uuid';
6
6
import * as common from '@module/common/common-index.js' ;
7
7
import { createRandomServerId } from '@module/utils/mysql-utils.js' ;
8
8
import { TableMapEntry } from '@powersync/mysql-zongji' ;
9
+ import crypto from 'crypto' ;
9
10
10
11
describe ( 'BinlogListener tests' , ( ) => {
11
- const MAX_QUEUE_SIZE = 10 ;
12
+ const MAX_QUEUE_CAPACITY_MB = 1 ;
12
13
const BINLOG_LISTENER_CONNECTION_OPTIONS = {
13
14
...TEST_CONNECTION_OPTIONS ,
14
- max_binlog_queue_size : MAX_QUEUE_SIZE
15
+ binlog_queue_memory_limit : MAX_QUEUE_CAPACITY_MB
15
16
} ;
16
17
17
18
let connectionManager : MySQLConnectionManager ;
@@ -22,7 +23,7 @@ describe('BinlogListener tests', () => {
22
23
connectionManager = new MySQLConnectionManager ( BINLOG_LISTENER_CONNECTION_OPTIONS , { } ) ;
23
24
const connection = await connectionManager . getConnection ( ) ;
24
25
await clearTestDb ( connection ) ;
25
- await connection . query ( `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY, description text )` ) ;
26
+ await connection . query ( `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY, description MEDIUMTEXT )` ) ;
26
27
connection . release ( ) ;
27
28
const fromGTID = await getFromGTID ( connectionManager ) ;
28
29
@@ -52,24 +53,30 @@ describe('BinlogListener tests', () => {
52
53
expect ( queueStopSpy ) . toHaveBeenCalled ( ) ;
53
54
} ) ;
54
55
55
- test ( 'Pause Zongji binlog listener when processing queue reaches max size' , async ( ) => {
56
+ test ( 'Pause Zongji binlog listener when processing queue reaches maximum memory size' , async ( ) => {
56
57
const pauseSpy = vi . spyOn ( binLogListener . zongji , 'pause' ) ;
57
58
const resumeSpy = vi . spyOn ( binLogListener . zongji , 'resume' ) ;
58
- const queueSpy = vi . spyOn ( binLogListener . processingQueue , 'length' ) ;
59
59
60
- const ROW_COUNT = 100 ;
60
+ // Pause the event handler to force a backlog on the processing queue
61
+ eventHandler . pause ( ) ;
62
+
63
+ const ROW_COUNT = 10 ;
61
64
await insertRows ( connectionManager , ROW_COUNT ) ;
62
65
63
66
const startPromise = binLogListener . start ( ) ;
64
67
68
+ // Wait for listener to pause due to queue reaching capacity
69
+ await vi . waitFor ( ( ) => expect ( pauseSpy ) . toHaveBeenCalled ( ) , { timeout : 5000 } ) ;
70
+
71
+ expect ( binLogListener . isQueueOverCapacity ( ) ) . toBeTruthy ( ) ;
72
+ // Resume event processing
73
+ eventHandler . unpause ! ( ) ;
74
+
65
75
await vi . waitFor ( ( ) => expect ( eventHandler . rowsWritten ) . equals ( ROW_COUNT ) , { timeout : 5000 } ) ;
66
76
binLogListener . stop ( ) ;
67
77
await expect ( startPromise ) . resolves . toBeUndefined ( ) ;
68
-
69
- // Count how many times the queue reached the max size. Consequently, we expect the listener to have paused and resumed that many times.
70
- const overThresholdCount = queueSpy . mock . results . map ( ( r ) => r . value ) . filter ( ( v ) => v === MAX_QUEUE_SIZE ) . length ;
71
- expect ( pauseSpy ) . toHaveBeenCalledTimes ( overThresholdCount ) ;
72
- expect ( resumeSpy ) . toHaveBeenCalledTimes ( overThresholdCount ) ;
78
+ // Confirm resume was called after unpausing
79
+ expect ( resumeSpy ) . toHaveBeenCalled ( ) ;
73
80
} ) ;
74
81
75
82
test ( 'Binlog events are correctly forwarded to provided binlog events handler' , async ( ) => {
@@ -101,7 +108,9 @@ async function getFromGTID(connectionManager: MySQLConnectionManager) {
101
108
102
109
async function insertRows ( connectionManager : MySQLConnectionManager , count : number ) {
103
110
for ( let i = 0 ; i < count ; i ++ ) {
104
- await connectionManager . query ( `INSERT INTO test_DATA(id, description) VALUES('${ uuid ( ) } ','test${ i } ')` ) ;
111
+ await connectionManager . query (
112
+ `INSERT INTO test_DATA(id, description) VALUES('${ uuid ( ) } ','test${ i } ${ crypto . randomBytes ( 100_000 ) . toString ( 'hex' ) } ')`
113
+ ) ;
105
114
}
106
115
}
107
116
@@ -119,7 +128,19 @@ class TestBinLogEventHandler implements BinLogEventHandler {
119
128
rowsDeleted = 0 ;
120
129
commitCount = 0 ;
121
130
131
+ unpause : ( ( value : void | PromiseLike < void > ) => void ) | undefined ;
132
+ private pausedPromise : Promise < void > | undefined ;
133
+
134
+ pause ( ) {
135
+ this . pausedPromise = new Promise ( ( resolve ) => {
136
+ this . unpause = resolve ;
137
+ } ) ;
138
+ }
139
+
122
140
async onWrite ( rows : Row [ ] , tableMap : TableMapEntry ) {
141
+ if ( this . pausedPromise ) {
142
+ await this . pausedPromise ;
143
+ }
123
144
this . rowsWritten = this . rowsWritten + rows . length ;
124
145
}
125
146
0 commit comments