Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 58 additions & 15 deletions src/database/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const DATABASE_FILENAME = "achievements.sqlite";
// Database connection object
let DB: Database | null = null;
let SQL: SqlJsStatic | null = null;
// Flag to prevent database saves during initialization
let isInitializing = false;

// ================== MODULE FUNCTIONS ==================

Expand All @@ -48,6 +50,16 @@ export namespace db_model {
return DB;
}

/**
* Check if the database is ready for operations.
* Returns false while initialization is in progress.
*
* @returns {boolean} True if database is fully initialized and ready
*/
export function isReady(): boolean {
return !isInitializing && DB !== null;
}

/**
* Initialize the database.
*
Expand All @@ -61,7 +73,7 @@ export namespace db_model {
"node_modules",
"sql.js",
"dist",
"sql-wasm.wasm"
"sql-wasm.wasm",
);

SQL = await initSqlJs({
Expand Down Expand Up @@ -91,11 +103,17 @@ export namespace db_model {

/**
* Save the database to disk.
* Will not save if in readonly mode.
* Will not save if in readonly mode or during initialization.
*
* @returns {Promise<void>}
*/
export async function saveDB(): Promise<void> {
// Prevent saves during database initialization to avoid partial/corrupted state
if (isInitializing) {
logger.debug("Skipping database save - database is still initializing");
return;
}

if (db_lock.isReadOnly()) {
logger.debug("Skipping database save - running in readonly mode");
return;
Expand Down Expand Up @@ -126,23 +144,33 @@ export namespace db_model {
*/
export async function activate(
context: vscode.ExtensionContext,
dbPath?: string
dbPath?: string,
): Promise<boolean> {
let databaseDir = context.globalStorageUri.fsPath;
await fs.promises.mkdir(databaseDir, { recursive: true });
DATABASE_PATH = dbPath || path.join(databaseDir, DATABASE_FILENAME);
// Set flag to prevent saves during initialization
isInitializing = true;
let hasLock = false;

try {
let databaseDir = context.globalStorageUri.fsPath;
await fs.promises.mkdir(databaseDir, { recursive: true });
DATABASE_PATH = dbPath || path.join(databaseDir, DATABASE_FILENAME);

// Try to acquire the database lock
const hasLock = await db_lock.acquireLock(DATABASE_PATH);
// Try to acquire the database lock
hasLock = await db_lock.acquireLock(DATABASE_PATH);

await init(context);
await init(context);

if (DB) {
// Only apply migrations and init data if we have write access
if (hasLock) {
// @ts-ignore
await applyMigration(DB, -1);
await db_init.activate();
if (DB) {
// Only apply migrations and init data if we have write access
if (hasLock) {
await applyMigration(DB, -1);
await db_init.activate();
}
}
} finally {
// Mark database as ready and perform final save if we have write access
isInitializing = false;
if (hasLock && DB && !db_lock.isReadOnly()) {
await saveDB();
}
}
Expand All @@ -161,6 +189,9 @@ export namespace db_model {
* @returns {Promise<void>}
*/
export async function deactivate(): Promise<void> {
// Reset initialization flag on deactivation
isInitializing = false;

if (DB) {
try {
DB.close();
Expand All @@ -175,6 +206,18 @@ export namespace db_model {
await db_lock.releaseLock();
}

/**
* Reset module state for testing purposes.
* This function should only be used in tests.
*
* @internal
*/
export function _resetState(): void {
isInitializing = false;
DB = null;
SQL = null;
}

/**
* Execute a query and return all rows as objects.
*
Expand Down
Loading