dotnet / dotnet/efcore

Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 14: 'unable to open database file'

Open
#34,273 7 comments 3 reactions 0 assignees View on GitHub
area-external area-sqlite customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

## Ask a question

I am seeing the following error sporadically.

```
An error occurred using the connection to database 'main' on server 'path\to\db'.

Exception:
Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 14: 'unable to open database file'.
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteConnectionInternal..ctor(SqliteConnectionStringBuilder connectionOptions, SqliteConnectionPool pool)
at Microsoft.Data.Sqlite.SqliteConnectionFactory.GetConnection(SqliteConnection outerConnection)
at Microsoft.Data.Sqlite.SqliteConnection.Open()
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnection(Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean errorsExpected)
```

I am using pooled database context and factory. My assumption here is that concurrent write-operations is causing the issue. Or as far as I understand, as the DB is locked during a write-operation, a read-operation at the same, can also cause the issue.

As I cannot deterministically reproduce the issue, I would like to ask if anyone can point me in the direction, to better debug and solve this issue.

### Include your code

#### Program.cs

```C#
services
.AddDbContextPool((serviceProvider, options) =>
{
Directory.CreateDirectory(ApplicationDataPath);
options.UseSqlite($"Data Source={Path.Combine(ApplicationDataPath, "data.db")}");
options.AddInterceptors(serviceProvider.GetRequiredService());
})
.AddPooledDbContextFactory(options => { });
services.AddHostedService();
```

#### DatabaseContext.cs

```cs
using Microsoft.EntityFrameworkCore;
using SQLitePCL;

public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions options) : base(options) { }

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
Batteries.Init();
}
}
```

#### DatabaseMigrationService.cs

```cs
public class DatabaseMigrationService : IHostedLifecycleService
{
private readonly IDbContextFactory _DbContextFactory;

public DatabaseMigrationService(IDbContextFactory dbContextFactory)
{
_DbContextFactory = dbContextFactory;
}

public async Task StartingAsync(CancellationToken cancellationToken)
{
await using var ctx = await _DbContextFactory.CreateDbContextAsync(cancellationToken);
await ctx.Database.MigrateAsync(cancellationToken);
}

public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
```

As there is another background service that runs tasks periodically (also a `IHostedLifecycleService`, and uses the `StartedAsync` hook), any timing conflict during the startup of the service should be avoided.

#### DatabaseConnectionInterceptor.cs

```cs
public class DatabaseConnectionInterceptor : DbConnectionInterceptor
{
private readonly ILogger _Logger;

public DatabaseConnectionInterceptor(ILogger logger)
{
_Logger = logger;
}

public override void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventData)
{
base.ConnectionFailed(connection, eventData);
Log(eventData);
}

public override async Task ConnectionFailedAsync(DbConnection connection, ConnectionErrorEventData eventData, CancellationToken cancellationToken = default)
{
await base.ConnectionFailedAsync(connection, eventData, cancellationToken);
Log(eventData);
}

private void Log(ConnectionErrorEventData eventData) => _Logger.LogWarning(WarningEvents.DatabaseConnectionFailed, eventData.Exception, "Connection failed: {Error}", eventData.ToString());
}
```

### Include stack traces

See above.

### Include verbose output

NA

### Include provider and version information

EF Core version: 8.0.7
Database provider: Microsoft.EntityFrameworkCore.Sqlite 8.0.7
Target framework: net 8
Operating system: Windows Server
IDE: Visual Studio 2022 17.4

### Additional details and questions

- I have a queuing service, that writes to a database table. The same service is also periodically triggered to process the queued stuff that involves both read and write.
- On the other hand, there are endpoints with high traffic that involves only (happy-path) reading from the database. One of these endpoints uses the queuing service to write to the database.
- My assumption is that when the queuing service locks the DB for the write operation, any other attempt to establish a connection fails. Is this assumption unfounded?
- I am inclining towards employing a semaphoreslim in the queuing service for the db operations. However, I don't want to write noisy code in every other endpoints to check the semaphore status and perform operations conditionally or wait. Is there a better way?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.