This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-room-sqlite-concurrency-isolation-standard

Kotlin Multiplatform Room SQLite Concurrency & Serial Queue Isolation Standard

Enforces strict serialized write isolation and pool-managed read concurrency across Android ART and iOS Darwin runtimes using Room KMP. Prevents SQLite 'database is locked' errors (SQLITE_BUSY 5), native thread deadlocks, and dirty reads by binding RoomDatabase builders to dedicated single-threaded write dispatchers and bounded multi-threaded read pools.

Prohibited Anti-Pattern: Unconfined Concurrent SQLite Mutations Across Native Thread Boundaries Inducing SQLITE_BUSY Deadlocks and POSIX Signal Aborts

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform Room SQLite Concurrency & Serial Queue Isolation Standard - Enforces strict serialized write isolation and pool-managed read concurrency across Android ART and iOS Darwin runtimes using Room KMP. Prevents SQLite 'database is locked' errors (SQLITE_BUSY 5), native thread deadlocks, and dirty reads by binding RoomDatabase builders to dedicated single-threaded write dispatchers and bounded multi-threaded read pools.
globs: **/database/**, **/db/**, **/*Database*.kt, **/*Dao*.kt, **/di/**DatabaseModule*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-room-sqlite-concurrency-isolation-standard"
---

# Kotlin Multiplatform Room SQLite Concurrency & Serial Queue Isolation Standard

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Unconfined Concurrent SQLite Mutations Across Native Thread Boundaries Inducing SQLITE_BUSY Deadlocks and POSIX Signal Aborts

## System Constraints
You are a Principal Mobile Architect enforcing the Kotlin Multiplatform Room SQLite Concurrency Isolation Standard. You must mandate strict thread confinement for Room KMP database drivers across Android and iOS Darwin runtimes. Reject any code constructing RoomDatabase instances without an isolated single-threaded write dispatcher (such as CoroutineDispatcher backed by a serial queue or newSingleThreadContext), using unconfined dispatchers for write transactions, or allowing simultaneous multi-threaded writes against SQLite databases configured without WAL mode or proper busy timeouts.

## Strict Guidelines
- Engineers MUST ALWAYS configure RoomDatabase.Builder using .setQueryCoroutineContext() backed by a controlled CoroutineDispatcher hierarchy that strictly isolates mutative transactions to a single-threaded CoroutineDispatcher across both Android and iOS targets.
- Engineers MUST ALWAYS enable SQLite Write-Ahead Logging (WAL) and set a non-zero busy timeout (minimum 3000ms via PRAGMA busy_timeout = 3000) during database initialization on Darwin and Linux targets to mitigate cross-process/cross-thread lock contention.
- Engineers MUST NOT execute database write operations (insert, update, delete, or @Transaction blocks modifying state) within Dispatchers.Default or Dispatchers.IO directly without confining them to the serialized write dispatcher.
- Code SHOULD expose read queries as Kotlin Flows using Room's native reactive invalidation tracking, dispatched over an asynchronous read pool (Dispatchers.IO on Android or a multi-threaded dispatch pool on Native).
- Components NEVER instantiate multiple instances of RoomDatabase pointing to the same SQLite physical file within the application lifecycle; all access must be mediated through an application-scoped singleton managed via Dependency Injection.

## Bad vs Good Implementations
### Room KMP Database Construction and Serial Dispatcher Binding

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Failing to explicitly configure the Room coroutine context and write dispatcher causes SQLite statements to be invoked across disparate native threads. On iOS Darwin with POSIX pthread models, concurrent write transactions trigger SQLITE_BUSY (database is locked) exceptions or fatal process halts due to unhandled SQLite locking invariants.
```kotlin
// ANTI-PATTERN: Default builder configuration allowing unconfined concurrent writes across Darwin and ART runtimes
fun createDatabase(builder: RoomDatabase.Builder<AppDatabase>): AppDatabase {
    return builder
        // Missing setQueryCoroutineContext with dedicated dispatchers
        // Default dispatching causes concurrent native thread writes -> SQLITE_BUSY / POSIX deadlocks
        .build()
}

class Repository(private val db: AppDatabase) {
    suspend fun saveRecords(items: List<RecordEntity>) = withContext(Dispatchers.Default) {
        // Unconfined transactional write concurrently executed across multiple threads
        db.recordDao().insertAll(items)
    }
}
```

#### Production Standard (Correct)
> **Standard**: Configures BundledSQLiteDriver with explicit WAL PRAGMAs and a 5000ms busy timeout, while forcing all database query and transaction executions through a deterministic single-threaded serial dispatcher. This guarantees total write serialization and prevents multi-threaded locking race conditions between Android ART and iOS Darwin runtimes.
```kotlin
// PRODUCTION STANDARD: Strictly isolated single-thread writer and pooled reader context
import androidx.room.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import java.util.concurrent.Executors

val databaseWriteDispatcher = Executors.newSingleThreadExecutor { runnable ->
    Thread(runnable, "room-sqlite-writer").apply { isDaemon = true }
}.asCoroutineDispatcher()

fun createDatabase(builder: RoomDatabase.Builder<AppDatabase>): AppDatabase {
    return builder
        .setDriver(BundledSQLiteDriver())
        // Bind write isolation directly to the serial dispatcher, read pool falls back to IO
        .setQueryCoroutineContext(databaseWriteDispatcher)
        .addCallback(object : RoomDatabase.Callback() {
            override fun onOpen(connection: androidx.sqlite.SQLiteConnection) {
                // Enable WAL and lock timeout for non-blocking concurrent reads
                connection.execSQL("PRAGMA journal_mode = WAL;")
                connection.execSQL("PRAGMA busy_timeout = 5000;")
                connection.execSQL("PRAGMA synchronous = NORMAL;")
            }
        })
        .build()
}

class Repository(private val db: AppDatabase) {
    suspend fun saveRecords(items: List<RecordEntity>) = withContext(databaseWriteDispatcher) {
        db.useWriterConnection { 
            db.recordDao().insertAll(items)
        }
    }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Multiplatform Room SQLite Concurrency & Serial Queue Isolation Standard

Kotlin Multiplatform Room SQLite Concurrency & Serial Queue Isolation Standard defines an industrial-grade behavioral standard for mobile engineering. Enforces strict serialized write isolation and pool-managed read concurrency across Android ART and iOS Darwin runtimes using Room KMP. Prevents SQLite 'database is locked' errors (SQLITE_BUSY 5), native thread deadlocks, and dirty reads by binding RoomDatabase builders to dedicated single-threaded write dispatchers and bounded multi-threaded read pools. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Unconfined Concurrent SQLite Mutations Across Native Thread Boundaries Inducing SQLITE_BUSY Deadlocks and POSIX Signal Aborts Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftUnconfined Concurrent SQLite Mutations Across Native Thread Boundaries Inducing SQLITE_BUSY Deadlocks and POSIX Signal AbortsCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS configure RoomDatabase.Builder using .setQueryCoroutineContext() backed by a controlled CoroutineDispatcher hierarchy that strictly isolates mutative transactions to a single-threaded CoroutineDispatcher across both Android and iOS targets.

Engineers MUST ALWAYS configure RoomDatabase.Builder using .setQueryCoroutineContext() backed by a controlled CoroutineDispatcher hierarchy that strictly isolates mutative transactions to a single-threaded CoroutineDispatcher across both Android and iOS targets.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST ALWAYS enable SQLite Write-Ahead Logging (WAL) and set a non-zero busy timeout (minimum 3000ms via PRAGMA busy_timeout = 3000) during database initialization on Darwin and Linux targets to mitigate cross-process/cross-thread lock contention.

Engineers MUST ALWAYS enable SQLite Write-Ahead Logging (WAL) and set a non-zero busy timeout (minimum 3000ms via PRAGMA busy_timeout = 3000) during database initialization on Darwin and Linux targets to mitigate cross-process/cross-thread lock contention.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST NOT execute database write operations (insert, update, delete, or @Transaction blocks modifying state) within Dispatchers.Default or Dispatchers.IO directly without confining them to the serialized write dispatcher.

Engineers MUST NOT execute database write operations (insert, update, delete, or @Transaction blocks modifying state) within Dispatchers.Default or Dispatchers.IO directly without confining them to the serialized write dispatcher.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Code SHOULD expose read queries as Kotlin Flows using Room's native reactive invalidation tracking, dispatched over an asynchronous read pool (Dispatchers.IO on Android or a multi-threaded dispatch pool on Native).

Code SHOULD expose read queries as Kotlin Flows using Room's native reactive invalidation tracking, dispatched over an asynchronous read pool (Dispatchers.IO on Android or a multi-threaded dispatch pool on Native).

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Components NEVER instantiate multiple instances of RoomDatabase pointing to the same SQLite physical file within the application lifecycle

Components NEVER instantiate multiple instances of RoomDatabase pointing to the same SQLite physical file within the application lifecycle; all access must be mediated through an application-scoped singleton managed via Dependency Injection.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Kotlin Multiplatform Room SQLite Concurrency & Serial Queue Isolation Standard critical for AI coding assistants?

Enforces strict serialized write isolation and pool-managed read concurrency across Android ART and iOS Darwin runtimes using Room KMP. Prevents SQLite 'database is locked' errors (SQLITE_BUSY 5), native thread deadlocks, and dirty reads by binding RoomDatabase builders to dedicated single-threaded write dispatchers and bounded multi-threaded read pools. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Unconfined Concurrent SQLite Mutations Across Native Thread Boundaries Inducing SQLITE_BUSY Deadlocks and POSIX Signal Aborts) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Kotlin