@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.
@@ -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.
@@ -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 | });