@docstack/pouchdb-adapter-googledrive 0.0.9 → 0.1.0
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/package.json +1 -1
- package/compaction_test.log +0 -725
- package/docs/DEBUG_BIDIRECTIONAL_REPLICATION.ipynb +0 -481
- package/docs/DOCUMENTATION.md +0 -115
- package/docs/PROMISE_SUPPORT.md +0 -75
- package/docs/REPLICATION_ISSUE.md +0 -63
- package/emulation-test.log +0 -163
- package/failing_test.log +0 -31
- package/final_test.log +0 -2620
- package/live_test.log +0 -1209
- package/test_output.log +0 -563
- package/test_output_2.log +0 -1408
- package/test_output_3.log +0 -2445
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Root Cause Analysis: Replication FROM Google Drive Issues
|
|
2
|
-
|
|
3
|
-
After extensive debugging with a live emulation test suite, multiple critical issues were identified in the `_changes` and `notifyListeners` implementations.
|
|
4
|
-
|
|
5
|
-
## 1. Missing `seq` Property in Notifications (CRITICAL)
|
|
6
|
-
**Location**: `src/drive.ts` -> `notifyListeners()`
|
|
7
|
-
|
|
8
|
-
**Problem**:
|
|
9
|
-
The `notifyListeners` method was emitting change objects that included `id`, `rev`, and `deleted`, but omitted the `seq` (sequence number).
|
|
10
|
-
|
|
11
|
-
**Consequence**:
|
|
12
|
-
PouchDB's replication engine uses the `seq` to track checkpoints. If a change arrives without a `seq`, PouchDB silently ignores it or fails to update its "since" pointer, meaning the replication appears to "detect" a change but never actually processes the document.
|
|
13
|
-
|
|
14
|
-
**Fix**:
|
|
15
|
-
Updated the notification object to include the correct sequence number from the index.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 2. Infinite Reconnect Loop in Live Changes
|
|
20
|
-
**Location**: `src/adapter.ts` -> `_changes()`
|
|
21
|
-
|
|
22
|
-
**Problem**:
|
|
23
|
-
An initial "fix" (based on standard CouchDB behavior) suggested calling `opts.complete()` at the end of the initial changes batch, even for live feeds.
|
|
24
|
-
|
|
25
|
-
**Consequence**:
|
|
26
|
-
In PouchDB, calling `complete()` on a **live** stream signals that the stream has ended gracefully. Because the replication is set to `live: true`, PouchDB interprets this as a connection drop and immediately restarts the changes feed. This created an infinite loop of:
|
|
27
|
-
`Connect -> Fetch Batch -> Call Complete -> Disconnect -> Reconnect`
|
|
28
|
-
This loop caused high CPU usage and eventually a "JavaScript heap out of memory" crash.
|
|
29
|
-
|
|
30
|
-
**Fix**:
|
|
31
|
-
Restricted `opts.complete()` to only be called if `!opts.live`.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## 3. System Document Leakage (`_local/`)
|
|
36
|
-
**Location**: `src/adapter.ts` -> `_changes()`
|
|
37
|
-
|
|
38
|
-
**Problem**:
|
|
39
|
-
PouchDB uses local documents (IDs starting with `_local/`) to store replication checkpoints. Our adapter was incorrectly including these internal documents in the `_changes` feed results.
|
|
40
|
-
|
|
41
|
-
**Consequence**:
|
|
42
|
-
When the remote side (Google Drive) reported a change to a `_local/` document, the local PouchDB tried to fetch it to replicate it. However, local documents are non-replicable and often missing from the full document fetch logic, leading to `not_found: missing` errors that crashed the replication process.
|
|
43
|
-
|
|
44
|
-
**Fix**:
|
|
45
|
-
Added a filter to both the initial batch and the live listener to ignore any document ID starting with `_local/`.
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 4. Inefficient Polling & Redundant `load()` Calls
|
|
50
|
-
**Location**: `src/drive.ts` -> `startPolling()` / `load()`
|
|
51
|
-
|
|
52
|
-
**Problem**:
|
|
53
|
-
The polling mechanism was frequently triggering `load()`, which cleared the entire `processedLogIds` cache and re-downloaded all change logs from Google Drive. Additionally, `load()` was being called on every conflict retry without checking if the metadata had actually changed.
|
|
54
|
-
|
|
55
|
-
**Fix**:
|
|
56
|
-
1. Implemented **ETag-based change detection** in polling.
|
|
57
|
-
2. Added an **ETag check at the start of `load()`** to skip processing if the metadata on the server haven't changed since the last load.
|
|
58
|
-
3. Updated `tryAppendChanges` to proactively call `notifyListeners()` for local writes, reducing reliance on the next polling tick for local UI updates.
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Summary of Results
|
|
63
|
-
With these fixes applied, bidirectional replication is now stable and efficient. The "Out of Memory" crashes have been resolved, and changes from Google Drive are correctly detected and propagated to the local database within the specified polling interval.
|
package/emulation-test.log
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @docstack/pouchdb-adapter-googledrive@0.0.7 test
|
|
3
|
-
> jest tests/emulation.test.ts
|
|
4
|
-
|
|
5
|
-
console.log
|
|
6
|
-
Test Drive Server running on port 46325
|
|
7
|
-
|
|
8
|
-
at Server.<anonymous> (src/test-utils/server.ts:47:25)
|
|
9
|
-
|
|
10
|
-
console.log
|
|
11
|
-
Server started on port 46325
|
|
12
|
-
|
|
13
|
-
at Object.<anonymous> (tests/emulation.test.ts:22:17)
|
|
14
|
-
|
|
15
|
-
console.log
|
|
16
|
-
GET /drive/v3/files
|
|
17
|
-
|
|
18
|
-
at src/test-utils/server.ts:23:21
|
|
19
|
-
|
|
20
|
-
console.log
|
|
21
|
-
Self-test fetch status: 200
|
|
22
|
-
|
|
23
|
-
at Object.<anonymous> (tests/emulation.test.ts:25:21)
|
|
24
|
-
|
|
25
|
-
console.log
|
|
26
|
-
Initializing DriveHandler with options: {
|
|
27
|
-
adapter: 'googledrive',
|
|
28
|
-
testMode: true,
|
|
29
|
-
testServerUrl: 'http://127.0.0.1:46325',
|
|
30
|
-
accessToken: 'mock-token',
|
|
31
|
-
deterministic_revs: true,
|
|
32
|
-
name: 'testdb'
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
at PouchDB.GoogleDrivePouch (src/adapter.ts:42:17)
|
|
36
|
-
|
|
37
|
-
console.log
|
|
38
|
-
[googledrive-drive] [testdb] Loading database, options {
|
|
39
|
-
options: {
|
|
40
|
-
adapter: 'googledrive',
|
|
41
|
-
testMode: true,
|
|
42
|
-
testServerUrl: 'http://127.0.0.1:46325',
|
|
43
|
-
accessToken: 'mock-token',
|
|
44
|
-
deterministic_revs: true,
|
|
45
|
-
name: 'testdb'
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
at DriveHandler.log (src/drive.ts:68:17)
|
|
50
|
-
|
|
51
|
-
console.log
|
|
52
|
-
GET /drive/v3/files?q=name%20%3D%20%27testdb%27%20and%20mimeType%20%3D%20%27application%2Fvnd.google-apps.folder%27%20and%20trashed%20%3D%20false&fields=files%28id%2Cname%2CmimeType%2Cparents%2CmodifiedTime%29
|
|
53
|
-
|
|
54
|
-
at src/test-utils/server.ts:23:21
|
|
55
|
-
|
|
56
|
-
console.log
|
|
57
|
-
POST /drive/v3/files?fields=id,modifiedTime
|
|
58
|
-
|
|
59
|
-
at src/test-utils/server.ts:23:21
|
|
60
|
-
|
|
61
|
-
console.log
|
|
62
|
-
[googledrive-drive] [testdb] Retrieved folder { folderId: 'r8bf1gdzs2' }
|
|
63
|
-
|
|
64
|
-
at DriveHandler.log (src/drive.ts:68:17)
|
|
65
|
-
|
|
66
|
-
console.log
|
|
67
|
-
GET /drive/v3/files?q=name%20%3D%20%27_meta.json%27%20and%20%27r8bf1gdzs2%27%20in%20parents%20and%20trashed%20%3D%20false&fields=files%28id%2Cname%2CmimeType%2Cparents%2CmodifiedTime%29
|
|
68
|
-
|
|
69
|
-
at src/test-utils/server.ts:23:21
|
|
70
|
-
|
|
71
|
-
console.log
|
|
72
|
-
[googledrive-drive] [testdb] Meta file not found, creating new
|
|
73
|
-
|
|
74
|
-
at DriveHandler.log (src/drive.ts:68:17)
|
|
75
|
-
|
|
76
|
-
console.log
|
|
77
|
-
GET /drive/v3/files?q=name%20%3D%20%27_meta.json%27%20and%20%27r8bf1gdzs2%27%20in%20parents%20and%20trashed%20%3D%20false&fields=files%28id%2Cname%2CmimeType%2Cparents%2CmodifiedTime%29
|
|
78
|
-
|
|
79
|
-
at src/test-utils/server.ts:23:21
|
|
80
|
-
|
|
81
|
-
console.log
|
|
82
|
-
POST /upload/drive/v3/files?uploadType=multipart&fields=id,modifiedTime
|
|
83
|
-
|
|
84
|
-
at src/test-utils/server.ts:23:21
|
|
85
|
-
|
|
86
|
-
console.log
|
|
87
|
-
[googledrive-drive] [testdb] Replaying change logs
|
|
88
|
-
|
|
89
|
-
at DriveHandler.log (src/drive.ts:68:17)
|
|
90
|
-
|
|
91
|
-
console.log
|
|
92
|
-
[googledrive-drive] [testdb] Polling disabled (no interval provided)
|
|
93
|
-
|
|
94
|
-
at DriveHandler.log (src/drive.ts:68:17)
|
|
95
|
-
|
|
96
|
-
console.log
|
|
97
|
-
POST /upload/drive/v3/files?uploadType=multipart&fields=id,modifiedTime
|
|
98
|
-
|
|
99
|
-
at src/test-utils/server.ts:23:21
|
|
100
|
-
|
|
101
|
-
console.log
|
|
102
|
-
GET /drive/v3/files?q=name%20%3D%20%27_meta.json%27%20and%20%27r8bf1gdzs2%27%20in%20parents%20and%20trashed%20%3D%20false&fields=files%28id%2Cname%2CmimeType%2Cparents%2CmodifiedTime%29
|
|
103
|
-
|
|
104
|
-
at src/test-utils/server.ts:23:21
|
|
105
|
-
|
|
106
|
-
console.log
|
|
107
|
-
PATCH /upload/drive/v3/files/u4t2406y2fh?uploadType=media&fields=id,modifiedTime
|
|
108
|
-
|
|
109
|
-
at src/test-utils/server.ts:23:21
|
|
110
|
-
|
|
111
|
-
console.log
|
|
112
|
-
Initializing DriveHandler with options: {
|
|
113
|
-
adapter: 'googledrive',
|
|
114
|
-
testMode: true,
|
|
115
|
-
testServerUrl: 'http://127.0.0.1:46325',
|
|
116
|
-
accessToken: 'mock-token',
|
|
117
|
-
deterministic_revs: true,
|
|
118
|
-
name: 'remote'
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
at PouchDB.GoogleDrivePouch (src/adapter.ts:42:17)
|
|
122
|
-
|
|
123
|
-
console.log
|
|
124
|
-
[googledrive-drive] [remote] Loading database, options {
|
|
125
|
-
options: {
|
|
126
|
-
adapter: 'googledrive',
|
|
127
|
-
testMode: true,
|
|
128
|
-
testServerUrl: 'http://127.0.0.1:46325',
|
|
129
|
-
accessToken: 'mock-token',
|
|
130
|
-
deterministic_revs: true,
|
|
131
|
-
name: 'remote'
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
at DriveHandler.log (src/drive.ts:68:17)
|
|
136
|
-
|
|
137
|
-
PASS tests/emulation.test.ts
|
|
138
|
-
Google Drive Emulation Integration
|
|
139
|
-
✓ should create database and write documents (139 ms)
|
|
140
|
-
✓ should replicate (11 ms)
|
|
141
|
-
|
|
142
|
-
Test Suites: 1 passed, 1 total
|
|
143
|
-
Tests: 2 passed, 2 total
|
|
144
|
-
Snapshots: 0 total
|
|
145
|
-
Time: 4.057 s
|
|
146
|
-
Ran all test suites matching tests/emulation.test.ts.
|
|
147
|
-
console.error
|
|
148
|
-
Failed to load database [Error: Network Error: fetch failed (GET http://127.0.0.1:46325/drive/v3/files?q=name%20%3D%20%27remote%27%20and%20mimeType%20%3D%20%27application%2Fvnd.google-apps.folder%27%20and%20trashed%20%3D%20false&fields=files%28id%2Cname%2CmimeType%2Cparents%2CmodifiedTime%29)] {
|
|
149
|
-
code: 'network_error',
|
|
150
|
-
url: 'http://127.0.0.1:46325/drive/v3/files?q=name%20%3D%20%27remote%27%20and%20mimeType%20%3D%20%27application%2Fvnd.google-apps.folder%27%20and%20trashed%20%3D%20false&fields=files%28id%2Cname%2CmimeType%2Cparents%2CmodifiedTime%29',
|
|
151
|
-
method: 'GET'
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
208 | }
|
|
155
|
-
209 | } catch (e) {
|
|
156
|
-
> 210 | console.error('Failed to load database', e);
|
|
157
|
-
| ^
|
|
158
|
-
211 | } finally {
|
|
159
|
-
212 | this.loadingPromise = null;
|
|
160
|
-
213 | }
|
|
161
|
-
|
|
162
|
-
at src/drive.ts:210:25
|
|
163
|
-
|
package/failing_test.log
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
● Concurrency & Race Conditions › should handle race condition when two clients append simultaneously
|
|
2
|
-
|
|
3
|
-
expect(received).toBe(expected) // Object.is equality
|
|
4
|
-
|
|
5
|
-
Expected: 2
|
|
6
|
-
Received: 1
|
|
7
|
-
|
|
8
|
-
107 | expect(doc1).toBeDefined();
|
|
9
|
-
108 | expect(doc2).toBeDefined();
|
|
10
|
-
> 109 | expect(driveHandler1.seq).toBe(2);
|
|
11
|
-
| ^
|
|
12
|
-
110 | });
|
|
13
|
-
111 |
|
|
14
|
-
112 | it('should detect conflicting updates', async () => {
|
|
15
|
-
|
|
16
|
-
at Object.<anonymous> (tests/concurrency_mock.test.ts:109:35)
|
|
17
|
-
|
|
18
|
-
● Concurrency & Race Conditions › should detect conflicting updates
|
|
19
|
-
|
|
20
|
-
expect(received).toBe(expected) // Object.is equality
|
|
21
|
-
|
|
22
|
-
Expected: 409
|
|
23
|
-
Received: undefined
|
|
24
|
-
|
|
25
|
-
122 | throw new Error('Should have thrown conflict');
|
|
26
|
-
123 | } catch (e: any) {
|
|
27
|
-
> 124 | expect(e.status).toBe(409); // Conflict
|
|
28
|
-
| ^
|
|
29
|
-
125 | }
|
|
30
|
-
126 | });
|
|
31
|
-
127 | });
|