8000 AccessViolation in SQLiteDistributedLock · Issue #79 · raisedapp/Hangfire.Storage.SQLite · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

AccessViolation in SQLiteDistributedLock #79

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

Open
ax0l0tl opened this issue Apr 3, 2025 · 0 comments
Open

AccessViolation in SQLiteDistributedLock #79

ax0l0tl opened this issue Apr 3, 2025 · 0 comments

Comments

@ax0l0tl
Copy link
ax0l0tl commented Apr 3, 2025

Version 0.4.2, .net 8:

We encounter AccessViolationExceptions in SQLiteDistributedLock.UpdateExpiration. Mainly in debug mode, but we also encouter problems with the release version, were hangfire storage gets corrupted which might be caused by this exception.

It looks like two threads are accessing the same SQLiteConnection instance at the same time. First one is the heartbeat timer from SQLiteDistributedLock the second one in our case is RecurringJobScheduler tryiing to Release another DistributedLock.

Image

Image

Thread 29892 and thread 34292 use the same connection instance.

I think this can always happen, because the connection is shared between the consumer of the lock an the lock itself.

Can be reproduced with the following test:

[Fact]
//this will result in an access violation exception caused by multithreaded usage of the same connection
//(distributed lock heartbeat is trying to update expiry on owners connection).
public void Use_Connection_When_Heartbeat_Fires()
{
    UseConnection(database =>
    {
        using var slock = SQLiteDistributedLock.Acquire("resource1", TimeSpan.FromSeconds(10), database, new SQLiteStorageOptions
        {
            DistributedLockLifetime = TimeSpan.FromSeconds(5) //heartbeat every second
        });        

        var start = DateTimeOffset.Now;
        //generate some pressure on connection to increase probability of race condition with heartbeat timer thread
        while (DateTimeOffset.Now - start < TimeSpan.FromMinutes(1)) 
        {
            database.Database.Insert(new JobParameter()
            {
                ExpireAt = start.AddSeconds(15).UtcDateTime,
                JobId = 13,
                Name = "MyParameter",
                Value = "MyValue"
            });
        }
    });
}

Using SQLiteOpenFlags.FullMutex is a workaround for the problem, but I think that should not be the final solution.

UseSQLiteStorage(
	new SQLiteDbConnectionFactory(() => new SQLiteConnection(
		databasePath: databasePath,
		openFlags: SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex,
		storeDateTimeAsTicks: true
		)
		{
			BusyTimeout = TimeSpan.FromSeconds(value: 10)
		}
	)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant
0