Skip to content

Commit d4c7035

Browse files
committed
read and merge other uuids before dumping
1 parent 0fb37f4 commit d4c7035

3 files changed

Lines changed: 120 additions & 7 deletions

File tree

prompt.md

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,77 @@ These enhancements provide a robust, flexible, and session-specific backup syste
262262
Notes for day 4:
263263
We are getting very close, now we just need to sync with the other sessions.
264264

265-
In the same loop as the dump of the db, we now have to first check the other `uuid`'s databases, and merge them with ours.
265+
In the same loop as the dump of the db, we now have to first check the other `uuid`'s databases, and merge them with ours. Like iterating over every folder under the `database` folder (exept ours).
266+
267+
This would require creating a new function to marge the data, and not use the restore database function.
268+
269+
I was checking JSDocs, and I like it, let's use it from now on (don't add it if the function doesn't have it, but for every new function yes).
270+
271+
--- day #4 start ---
272+
273+
# Backup Scheduler Enhancements Summary
274+
275+
This update to the `BackupScheduler` class in `backup-scheduler.ts` introduces two main improvements:
276+
277+
1. **Merging Backups from Other Sessions:**
278+
279+
- **What Changed:**
280+
Before performing the auto dump, the scheduler now calls `mergeOtherSessionBackups()`. This function:
281+
- Iterates through the `database` folder under the base backup directory.
282+
- Excludes the current session (using `this.#sessionId`).
283+
- For each other session, if a dump file (`database.yjs.db`) exists, it reads the file, converts it into a Yjs update, and applies it to the current Y.Doc.
284+
- **Why:**
285+
This allows merging of changes from multiple sessions (i.e., different installations/users) when syncing via a cloud storage solution.
286+
- **Key Variable/Method:**
287+
- `async mergeOtherSessionBackups(): Promise<void>`
288+
- Uses variables like:
289+
- `this.#baseBackupDirectory` (the original backup directory provided)
290+
- `this.#backupDirectory` (constructed as `path.join(newDir, 'database', this.#sessionId)`)
291+
- `this.#sessionId` (unique session identifier for the current installation)
292+
- `Y.applyUpdate(this.#ydoc, update);` (merges the update into the current document)
293+
294+
2. **Auto Dump Flow Update:**
295+
- **What Changed:**
296+
The `#startAutoDump()` method now calls `mergeOtherSessionBackups()` before dumping the current Y.Doc state.
297+
- **Why:**
298+
This ensures that any changes from other sessions are merged into the current document before the auto dump occurs.
299+
- **Key Variable/Method:**
300+
- The auto dump interval (`this.#dumpInterval`) now runs:
301+
```typescript
302+
await this.mergeOtherSessionBackups();
303+
const dumpFilePath = path.join(this.#backupDirectory, 'database.yjs.db');
304+
await this.backupToFile(dumpFilePath);
305+
```
306+
307+
## Variable Overview
308+
309+
- **`this.#ydoc`**:
310+
The Yjs document instance that holds all the application data.
311+
312+
- **`this.#baseBackupDirectory` & `this.#backupDirectory`**:
313+
314+
- `#baseBackupDirectory` is the provided base directory for backups.
315+
- `#backupDirectory` is the session-specific folder (`<baseBackupDirectory>/database/<sessionId>`) where backups are stored.
316+
317+
- **`this.#sessionId`**:
318+
A unique identifier for the current installation/session, used to segregate backups.
319+
320+
- **`SYNC_INTERVAL`**:
321+
A constant that determines the frequency (in milliseconds) of the auto dump process.
322+
323+
- **`this.#dumpInterval`**:
324+
The interval timer that triggers the auto dump (and merging process) periodically.
325+
326+
## Conclusion
327+
328+
These enhancements provide better observability of the Yjs documents state during backup operations and ensure that changes from multiple sessions are merged seamlessly. This is crucial for maintaining data consistency when files are synced via cloud services, allowing a more robust and traceable backup and merge strategy within OneFolder.
329+
330+
--- day #4 end ---
331+
332+
Notes for day 5:
333+
Yestarday I tested for the first time with two computers, and it the plan seems to work, is just that I found new problems.
334+
335+
- In the database files have a `relativePath` and a `absolutePath`, and the `absolutePath` is used more than 150 times across the app. But the `absolutePath` is different on each computer, breaking the thumbnails and a bunch of stuff in the way.
336+
- When importing a new location in computer2 it creates new `id`s for each image, which are not the same as the one in computer1, to kind of fix it I restored the database from computer1 to computer2, so they can have the same `id`. What we should do is that when importing a location, we first check if there isn't a db already that we can copy
337+
338+
Debuging this I realized that I have very little knowledge on what is going on in the DB, bc is hard to see, harder than a simple SQL. So I wanted to stop for a minute and make a "history" page in the app, where we can see all of the data flow.

src/backend/backup-scheduler.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function getWeekStart(): Date {
4646

4747
export default class BackupScheduler implements DataBackup {
4848
#ydoc: Y.Doc;
49+
#baseBackupDirectory: string;
4950
#backupDirectory: string;
5051
#sessionId: string;
5152
#lastBackupIndex: number = 0;
@@ -62,6 +63,7 @@ export default class BackupScheduler implements DataBackup {
6263
constructor(ydoc: Y.Doc, directory: string, sessionId: string) {
6364
this.#ydoc = ydoc;
6465
this.#sessionId = sessionId;
66+
this.#baseBackupDirectory = directory;
6567
this.#backupDirectory = path.join(directory, 'database', sessionId);
6668
fse.ensureDirSync(this.#backupDirectory);
6769
// Start the auto dump functionality independently.
@@ -91,6 +93,7 @@ export default class BackupScheduler implements DataBackup {
9193
* @param newDir The new base backup directory.
9294
*/
9395
async updateBackupDirectory(newDir: string): Promise<void> {
96+
this.#baseBackupDirectory = newDir;
9497
this.#backupDirectory = path.join(newDir, 'database', this.#sessionId);
9598
await fse.ensureDir(this.#backupDirectory);
9699
console.log('BackupScheduler: Updated backup directory to', this.#backupDirectory);
@@ -114,7 +117,7 @@ export default class BackupScheduler implements DataBackup {
114117
*/
115118
schedule(): void {
116119
if (Date.now() > this.#lastBackupDate.getTime() + AUTO_BACKUP_TIMEOUT) {
117-
this.#createPeriodicBackup();
120+
// this.#createPeriodicBackup();
118121
}
119122
}
120123

@@ -181,17 +184,20 @@ export default class BackupScheduler implements DataBackup {
181184

182185
/**
183186
* **Auto Dump Functionality:**
184-
* Starts an interval that dumps the entire Y.Doc state to a file named "database.yjs.db"
185-
* every SYNC_INTERVAL milliseconds. The file is overwritten on each dump.
187+
* Starts an interval that merges backups from other sessions and then dumps the entire Y.Doc state
188+
* to a file named "database.yjs.db" every SYNC_INTERVAL milliseconds.
189+
* The file is overwritten on each dump.
186190
*/
187191
#startAutoDump(): void {
188-
const dumpFilePath = path.join(this.#backupDirectory, 'database.yjs.db');
189192
this.#dumpInterval = setInterval(async () => {
190193
try {
194+
// Merge backups from other sessions before dumping our own state.
195+
await this.mergeOtherSessionBackups();
196+
const dumpFilePath = path.join(this.#backupDirectory, 'database.yjs.db');
191197
await this.backupToFile(dumpFilePath);
192198
console.log('Auto-dumped database to', dumpFilePath);
193199
} catch (error) {
194-
console.error('Error during auto dump:', error);
200+
console.error('Error during auto dump or merging other sessions:', error);
195201
}
196202
}, SYNC_INTERVAL);
197203
}
@@ -282,4 +288,38 @@ export default class BackupScheduler implements DataBackup {
282288

283289
return [tagsMap.size, filesMap.size];
284290
}
291+
292+
/**
293+
* Merges backup updates from all other sessions into the current Y.Doc.
294+
*
295+
* This method iterates over each session directory under the base backup directory (excluding the current session)
296+
* and, if a dump file ("database.yjs.db") exists, reads and applies the Yjs update to the current document.
297+
*
298+
* @returns A promise that resolves when all available session backups have been merged.
299+
*/
300+
async mergeOtherSessionBackups(): Promise<void> {
301+
const sessionsDir = path.join(this.#baseBackupDirectory, 'database');
302+
try {
303+
const sessions = await fse.readdir(sessionsDir);
304+
for (const session of sessions) {
305+
if (session === this.#sessionId) {
306+
continue;
307+
}
308+
const otherSessionBackupDir = path.join(sessionsDir, session);
309+
const dumpFilePath = path.join(otherSessionBackupDir, 'database.yjs.db');
310+
if (await fse.pathExists(dumpFilePath)) {
311+
try {
312+
const buffer = await fse.readFile(dumpFilePath);
313+
const update = new Uint8Array(buffer);
314+
Y.applyUpdate(this.#ydoc, update);
315+
console.log(`Merged backup from session ${session} from file ${dumpFilePath}`);
316+
} catch (err) {
317+
console.error(`Failed to merge backup from session ${session}:`, err);
318+
}
319+
}
320+
}
321+
} catch (err) {
322+
console.error('Error reading sessions directory:', err);
323+
}
324+
}
285325
}

src/frontend/containers/Settings/ImportExport.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export const ImportExport = observer(() => {
129129
className="btn-outlined"
130130
options={{
131131
properties: ['openFile'],
132-
filters: [{ extensions: ['json'], name: 'JSON' }],
132+
// filters: [{ extensions: ['json'], name: 'JSON' }],
133133
defaultPath: backupDir,
134134
}}
135135
onChange={handleChooseImportDir}

0 commit comments

Comments
 (0)