mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
The migration code import blocksuite in the dependency tree and prevent affine from running because electron's helper process tries to run browser only code that is part of the side effect of `@blocksuite/blocks`.  [The side effect free trick in esbuild config](https://github.com/toeverything/AFFiNE/pull/6415) does not clean up these - not sure why. It has been already 6 month since we introduced the migration code in DB. Instead of finding out the real root cause, I think may be better to remove the migration code completely so that no blocksuite code will be in the import paths in helper.js. 
247 lines
5.9 KiB
TypeScript
247 lines
5.9 KiB
TypeScript
import type { InsertRow } from '@affine/native';
|
|
import { SqliteConnection } from '@affine/native';
|
|
import type { ByteKVBehavior } from '@toeverything/infra/storage';
|
|
|
|
import { logger } from '../logger';
|
|
|
|
/**
|
|
* A base class for SQLite DB adapter that provides basic methods around updates & blobs
|
|
*/
|
|
export class SQLiteAdapter {
|
|
db: SqliteConnection | null = null;
|
|
constructor(public readonly path: string) {}
|
|
|
|
async connectIfNeeded() {
|
|
if (!this.db) {
|
|
this.db = new SqliteConnection(this.path);
|
|
await this.db.connect();
|
|
logger.info(`[SQLiteAdapter]`, 'connected:', this.path);
|
|
}
|
|
return this.db;
|
|
}
|
|
|
|
async destroy() {
|
|
const { db } = this;
|
|
this.db = null;
|
|
// log after close will sometimes crash the app when quitting
|
|
logger.info(`[SQLiteAdapter]`, 'destroyed:', this.path);
|
|
await db?.close();
|
|
}
|
|
|
|
async addBlob(key: string, data: Uint8Array) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.addBlob(key, data);
|
|
} catch (error) {
|
|
logger.error('addBlob', error);
|
|
}
|
|
}
|
|
|
|
async getBlob(key: string) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return null;
|
|
}
|
|
const blob = await this.db.getBlob(key);
|
|
return blob?.data ?? null;
|
|
} catch (error) {
|
|
logger.error('getBlob', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async deleteBlob(key: string) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.deleteBlob(key);
|
|
} catch (error) {
|
|
logger.error(`${this.path} delete blob failed`, error);
|
|
}
|
|
}
|
|
|
|
async getBlobKeys() {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return [];
|
|
}
|
|
return await this.db.getBlobKeys();
|
|
} catch (error) {
|
|
logger.error(`getBlobKeys failed`, error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async getUpdates(docId?: string) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return [];
|
|
}
|
|
return await this.db.getUpdates(docId);
|
|
} catch (error) {
|
|
logger.error('getUpdates', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async getAllUpdates() {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return [];
|
|
}
|
|
return await this.db.getAllUpdates();
|
|
} catch (error) {
|
|
logger.error('getAllUpdates', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// add a single update to SQLite
|
|
async addUpdateToSQLite(updates: InsertRow[]) {
|
|
// batch write instead write per key stroke?
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
const start = performance.now();
|
|
await this.db.insertUpdates(updates);
|
|
logger.debug(
|
|
`[SQLiteAdapter] addUpdateToSQLite`,
|
|
'length:',
|
|
updates.length,
|
|
'docids',
|
|
updates.map(u => u.docId),
|
|
performance.now() - start,
|
|
'ms'
|
|
);
|
|
} catch (error) {
|
|
logger.error('addUpdateToSQLite', this.path, error);
|
|
}
|
|
}
|
|
|
|
async deleteUpdates(docId?: string) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.deleteUpdates(docId);
|
|
} catch (error) {
|
|
logger.error('deleteUpdates', error);
|
|
}
|
|
}
|
|
|
|
async getUpdatesCount(docId?: string) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return 0;
|
|
}
|
|
return await this.db.getUpdatesCount(docId);
|
|
} catch (error) {
|
|
logger.error('getUpdatesCount', error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
async replaceUpdates(docId: string | null | undefined, updates: InsertRow[]) {
|
|
try {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.replaceUpdates(docId, updates);
|
|
} catch (error) {
|
|
logger.error('replaceUpdates', error);
|
|
}
|
|
}
|
|
|
|
serverClock: ByteKVBehavior = {
|
|
get: async key => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return null;
|
|
}
|
|
const blob = await this.db.getServerClock(key);
|
|
return blob?.data ?? null;
|
|
},
|
|
set: async (key, data) => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.setServerClock(key, data);
|
|
},
|
|
keys: async () => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return [];
|
|
}
|
|
return await this.db.getServerClockKeys();
|
|
},
|
|
del: async key => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.delServerClock(key);
|
|
},
|
|
clear: async () => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.clearServerClock();
|
|
},
|
|
};
|
|
|
|
syncMetadata: ByteKVBehavior = {
|
|
get: async key => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return null;
|
|
}
|
|
const blob = await this.db.getSyncMetadata(key);
|
|
return blob?.data ?? null;
|
|
},
|
|
set: async (key, data) => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.setSyncMetadata(key, data);
|
|
},
|
|
keys: async () => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return [];
|
|
}
|
|
return await this.db.getSyncMetadataKeys();
|
|
},
|
|
del: async key => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.delSyncMetadata(key);
|
|
},
|
|
clear: async () => {
|
|
if (!this.db) {
|
|
logger.warn(`${this.path} is not connected`);
|
|
return;
|
|
}
|
|
await this.db.clearSyncMetadata();
|
|
},
|
|
};
|
|
}
|