SQlite Retry logic for database lock
parent
ac9ca4df39
commit
d779175b90
|
@ -42,14 +42,16 @@ export class DatabaseService {
|
|||
* }
|
||||
*/
|
||||
public static async loadGameState(authorId: number): Promise<GameState | null> {
|
||||
const db = await DatabaseService.getDatabase();
|
||||
const sql = `SELECT data FROM game_state WHERE authorId = ? AND active = TRUE`;
|
||||
const row = await db.get(sql, [authorId]);
|
||||
if (row) {
|
||||
return JSON.parse(row.data) as GameState;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return this.withRetry(async () => {
|
||||
const db = await DatabaseService.getDatabase();
|
||||
const sql = `SELECT data FROM game_state WHERE authorId = ? AND active = TRUE`;
|
||||
const row = await db.get(sql, [authorId]);
|
||||
if (row) {
|
||||
return JSON.parse(row.data) as GameState;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,15 +65,17 @@ export class DatabaseService {
|
|||
* await DatabaseService.saveGameState(1, currentGameState);
|
||||
*/
|
||||
public static async saveGameState(authorId: number, gameState: GameState): Promise<void> {
|
||||
const db = await DatabaseService.getDatabase();
|
||||
const data = JSON.stringify(gameState);
|
||||
const sql = `
|
||||
return this.withRetry(async () => {
|
||||
const db = await DatabaseService.getDatabase();
|
||||
const data = JSON.stringify(gameState);
|
||||
const sql = `
|
||||
INSERT INTO game_state (authorId, data, active) VALUES (?, ?, TRUE)
|
||||
ON CONFLICT(authorId) DO UPDATE SET
|
||||
data = excluded.data
|
||||
WHERE active = TRUE;
|
||||
`;
|
||||
await db.run(sql, [authorId, data]);
|
||||
`;
|
||||
await db.run(sql, [authorId, data]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,9 +88,41 @@ export class DatabaseService {
|
|||
* await DatabaseService.resetGameState(1);
|
||||
*/
|
||||
public static async resetGameState(authorId: number): Promise<void> {
|
||||
const db = await DatabaseService.getDatabase();
|
||||
const sql = `UPDATE game_state SET active = FALSE WHERE authorId = ? AND active = TRUE`;
|
||||
await db.run(sql, [authorId]);
|
||||
return this.withRetry(async () => {
|
||||
const db = await this.getDatabase();
|
||||
const sql = `UPDATE game_state SET active = FALSE WHERE authorId = ? AND active = TRUE`;
|
||||
await db.run(sql, [authorId]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to execute a database operation with retries on failure.
|
||||
* This method is designed to handle SQLITE_BUSY errors by retrying the operation
|
||||
* with an exponential backoff strategy. This is particularly useful for handling
|
||||
* cases where the SQLite database is locked due to concurrent access attempts.
|
||||
*
|
||||
* @param operation A function that performs the database operation and returns a Promise.
|
||||
* @param maxRetries The maximum number of retries before giving up. Defaults to 5.
|
||||
* @param delay The initial delay (in milliseconds) before the first retry. This delay is doubled with each retry.
|
||||
* @returns A Promise that resolves with the result of the operation, or rejects if the operation fails after all retries.
|
||||
* @throws Will throw an error if the operation cannot be completed successfully within the allowed number of retries.
|
||||
*/
|
||||
private static async withRetry<T>(operation: () => Promise<T>, maxRetries: number = 5, delay: number = 100): Promise<T> {
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return await operation();
|
||||
} catch (error) {
|
||||
console.log('Error', error)
|
||||
// Type check or assertion to handle the error as an object with a 'code' property
|
||||
const e = error as { code?: string };
|
||||
if (e.code === 'SQLITE_BUSY') {
|
||||
console.log(`Database is busy, retrying... Attempt ${i + 1}`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i)));
|
||||
} else {
|
||||
throw error; // Rethrow if it's not an SQLITE_BUSY error
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error('Max retries reached for database operation');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue