Read the original article:Safe Database Initialization and Access in Multi-Threaded Mode
Context
Failed to initialize the database and obtain the context in multi-threaded mode when using relationalStore.getRdbStore(). The global cache context in multi-threading is empty, leading to a ReferenceError such as:
relationalStore.getRdbStore(SseSdk.getConfig()?.getContext()!, this.config)
Error message:
ReferenceError: xxxx is not initialized
Description
In multi-threaded scenarios, developers may attempt to initialize and access the database context from worker threads. However, getRdbStore does not support direct multi-threaded concurrent operations. This results in initialization exceptions and prevents safe access to the relational database.
Solution / Approach
-
Important Note:
getRdbStorecurrently supports only Promise and Callback asynchronous methods, not direct multi-threading. - The recommended approach is to:
- Use
async/awaitsyntax to encapsulate database operations. - Create and manage database operations through worker threads while passing the context properly.
- Use
registerGlobalCallObjectto share database objects between the main thread and worker threads safely.
- Use
Implementation Example:
Rdb.ets
export default class Rdb {
rdbStore?: relationalStore.RdbStore;
constructor(storeName: string, context: Context) {
const STORE_CONFIG: relationalStore.StoreConfig = {
name: storeName,
securityLevel: relationalStore.SecurityLevel.S1,
};
relationalStore.getRdbStore(context, STORE_CONFIG,
(err: BusinessError, rdbStore: relationalStore.RdbStore) => {
this.rdbStore = rdbStore;
if (err) {
console.error(`Get RdbStore failed, code: ${err.code}, message: ${err.message}`);
return;
}
console.info(`Get ${storeName} RdbStore successfully.`);
}
)
}
CreateTable() {
// Define table creation logic here
}
// Additional DB operations...
}
Index.ets
const workerInstance = new worker.ThreadWorker("entry/ets/workers/Worker.ets");
let context = getContext()
@Entry
@Component
struct Index {
build() {
Column() {
Button('Main thread creates Worker query').onClick(async () => {
let rdbTest = await new Rdb('Student.db', context)
await rdbTest.CreateTable()
await rdbTest.InsertData("Lisa", 12, 90.0)
workerInstance.registerGlobalCallObject("myObj", rdbTest)
workerInstance.postMessage(4)
})
Button('Worker creates database').onClick(() => {
workerInstance.postMessage(context)
})
Button('Worker creates table').onClick(() => {
workerInstance.postMessage(1)
})
Button('Worker inserts data').onClick(() => {
workerInstance.postMessage(2)
})
Button('Worker query data').onClick(() => {
workerInstance.postMessage(3)
})
}
.height('100%')
.width('100%')
}
}
Worker.ets
import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
import Rdb from '../pages/rdb';
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
let rdbTest: Rdb;
workerPort.onmessage = async (e: MessageEvents) => {
if (e.data == 1) {
await rdbTest.CreateTable()
} else if (e.data == 2) {
await rdbTest.InsertData("Lisa", 12, 90.0)
} else if (e.data == 3) {
await rdbTest.queryKeySync()
} else if (e.data == 4) {
try {
workerPort.callGlobalCallObjectMethod("myObj", "queryKeySync", 0)
} catch (error) {
console.error(`worker: error code ${error.code}, message: ${error.message}`)
}
} else {
rdbTest = await new Rdb('Student.db', e.data)
}
}
workerPort.onmessageerror = (e: MessageEvents) => {
console.log('onmessageerror', e)
}
workerPort.onerror = (e: ErrorEvent) => {
console.log('worker error', e)
}
Key Takeaways
-
relationalStore.getRdbStoredoes not support concurrent multi-threaded initialization. - Use async/await to manage asynchronous database operations.
- Database operations in worker threads should rely on context passing and global call object registration (
registerGlobalCallObject). - This ensures safe and consistent access to the relational database in multi-threaded environments.
Top comments (0)