Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sqlite: Deleting a database previously referenced by a different connection string throws #25797

Closed
ajcvickers opened this issue Aug 31, 2021 · 4 comments · Fixed by #25985 or #32615
Closed
Assignees
Labels
area-adonet-sqlite area-sqlite closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. punted-for-6.0 type-bug

Comments

@ajcvickers
Copy link
Member

I suspect this is because connection pooling still has a connection open for the database, but this is not discovered because the two connections use different connection strings. (One has Command Timeout set, the other does not.)

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
}

public class SomeContext : DbContext
{
    private readonly string _connectionString;

    public SomeContext(string connectionString)
    {
        _connectionString = connectionString;
    }

    public DbSet<User> Users { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlite(_connectionString);
}


public static class SqliteSamples
{
    public static void Main()
    {
        using (var context = new SomeContext("DataSource=test.db"))
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        }

        using (var context = new SomeContext("Command Timeout=60;DataSource=test.db"))
        {
            context.Database.EnsureDeleted(); // Throws here
            context.Database.EnsureCreated();
        }
    }
}
Unhandled exception. System.IO.IOException: The process cannot access the file 'C:\dotnet\efdocs\samples\core\Miscellaneous\NewInEFCore6\bin\Debug\net6.0\test.db' because it is being used by another process.
   at System.IO.FileSystem.DeleteFile(String fullPath) in System.Private.CoreLib.dll:token 0x6005f1a+0x15
   at System.IO.File.Delete(String path) in System.Private.CoreLib.dll:token 0x6005e48+0x0
   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteDatabaseCreator.Delete() in Microsoft.EntityFrameworkCore.Sqlite.dll:token 0x6000146+0x63
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureDeleted() in Microsoft.EntityFrameworkCore.Relational.dll:token 0x600057c+0x8
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureDeleted() in Microsoft.EntityFrameworkCore.dll:token 0x6001b36+0x0
   at SqliteSamples.Main() in C:\dotnet\efdocs\samples\core\Miscellaneous\NewInEFCore6\SqliteSamples.cs:line 41
@ajcvickers
Copy link
Member Author

Notes from triage: this could be fixed in EF Core by clearing all pools before deleting the file. However, it might also make sense for M.D.Sqlite to handle connections to the same file in some way.

@ajcvickers
Copy link
Member Author

Decision from triage: call ClearAllPools in EF.

@ajcvickers
Copy link
Member Author

Reverted to get test runs working reliably again.

@ajcvickers ajcvickers removed this from the 6.0.0-rc2 milestone Sep 16, 2021
@ajcvickers ajcvickers removed the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 16, 2021
@bricelam
Copy link
Contributor

A better fix might be to update the pooling code to use the same connection pool for the same database file as much as possible. In other words, it could normalize the path used in Data Source and ignore these connection string keywords:

  • Foreign Keys
  • Recursive Triggers
  • Default Timeout

@ajcvickers ajcvickers added this to the Backlog milestone Sep 17, 2021
@ajcvickers ajcvickers removed their assignment Sep 17, 2021
@bricelam bricelam self-assigned this Oct 27, 2021
@bricelam bricelam removed their assignment Jul 8, 2023
@ajcvickers ajcvickers self-assigned this Dec 14, 2023
@ajcvickers ajcvickers added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Dec 14, 2023
@ajcvickers ajcvickers modified the milestones: Backlog, 9.0.0 Dec 14, 2023
ajcvickers added a commit that referenced this issue Dec 14, 2023
Fixes #25797
Fixes #26016

I identified two race conditions, both caused by splitting state across multiple data structures. In particular, the Semaphore and the two ConcurrentStacks must stay in sync--that is, the Semaphore can let someone get a collection if and only if there is a connection available in the one of the stacks.

The fix is to wrap all these things in a single lock. It's possible that we don't need a full lock, but we already have one to protect _collections which can easily be expanded to cover the right areas.

Once this lock is used, the semaphore is no longer needed, and the stacks don't need to be concurrent because they are protected by the lock.
ajcvickers added a commit that referenced this issue Dec 14, 2023
Fixes #25797
Fixes #26016

I identified two race conditions, both caused by splitting state across multiple data structures. In particular, the Semaphore and the two ConcurrentStacks must stay in sync--that is, the Semaphore can let someone get a collection if and only if there is a connection available in the one of the stacks.

The fix is to wrap all these things in a single lock. It's possible that we don't need a full lock, but we already have one to protect _collections which can easily be expanded to cover the right areas.

Once this lock is used, the semaphore is no longer needed, and the stacks don't need to be concurrent because they are protected by the lock.
@ajcvickers ajcvickers modified the milestones: 9.0.0, 9.0.0-preview1 Jan 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-adonet-sqlite area-sqlite closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. punted-for-6.0 type-bug
Projects
None yet
2 participants