Quartz.NET + SQLite Jobstore выбрасывает JobPersistenceException

При добавлении или удалении задания в / из планировщика Quartz время от времени выдает исключение JobPersistenceException (следующее за предыдущим SQLiteException).

Вещи, которые кажутся заслуживающими внимания:

  • Quartz.NET 2.01 + System.Data.SQLite 1.0.66 (обе последние версии на момент написания только что заметили, что есть бинарный пакет для SQLite 1.0.82)
  • исключение также выдается, если в настоящее время не выполняется ни одно задание / триггер (я наблюдаю за прослушивателями Quartz)
  • задания добавляются вручную из контекста пользовательского интерфейса (мне нужно около 10-20 повторений, чтобы вызвать ошибку, но это кажется совершенно случайным)
  • Кажется, все работает нормально (несколько заданий, параллельное выполнение, постоянство после перезапуска приложения), пока я не касаюсь AddJob () / DeleteJob () После расширенного тестирования я уверен, что это не связано с добавление / удаление вакансий. Проблемы с блокировкой / доступом к базе данных являются общей проблемой.

Есть ли какая-либо рекомендуемая процедура, которую я не знаю, которую необходимо соблюдать при добавлении / удалении заданий?

Что-то не так с моей конфигурацией ISchedulerFactory? (см. ниже)

Дополнительные

  • Я пробовал использовать System.Data.SQLite 1.0.82, что только усугубило ситуацию. Я почти постоянно получаю сообщение «Ошибка SQLite (5): база данных заблокирована», как только Quartz выполняет задание.
  • Quartz.NET перечисляет System.Data.SQLite 1.0.56 в качестве поддерживаемого поставщика баз данных, поэтому при использовании более новой версии можно ожидать проблем. Однако я не рассматриваю вариант возврата с 1.0.66, поскольку в IIRC было внесено множество улучшений / исправлений.
  • Я взглянул на ствол разработки Quartz.NET между версией выпуска 2.0.1 (624) и текущей версией головы (669). Кажется, нет связанных исправлений.
  • Я подозреваю, что это проблема System.Data.SQLite. Я наткнулся на несколько сообщений (о разных версиях SQLite), в которых упоминалось, что могут быть некоторые проблемы с внутренним удалением ресурсов, при этом файл БД заблокирован.

Дополнение 2

На данный момент я отказался от этого. Я много чего пробовал, но разработка должна продолжаться. Я переключился на другой тип базы данных (Firebird), которая до сих пор, кажется, отлично работает с Quartz.

Если у кого-то это сработает, я все равно хотел бы об этом услышать.

-

Детали исключения:

Quartz.JobPersistenceException: «Не удалось зафиксировать транзакцию ADO.NET. Файл базы данных заблокирован \ r \ ndatabase заблокирована»

Куча

bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.ExecuteInNonManagedTXLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreTX.ExecuteInLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.RemoveJob(JobKey jobKey)
bei Quartz.Core.QuartzScheduler.DeleteJob(JobKey jobKey)
bei Quartz.Impl.StdScheduler.DeleteJob(JobKey jobKey)

InnerException SQLiteException: «Файл базы данных заблокирован \ r \ ndatabase заблокирована»

Куча

bei System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
bei System.Data.SQLite.SQLiteDataReader.NextResult()
bei System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
bei System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
bei System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
bei System.Data.SQLite.SQLiteTransaction.Commit()
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)

Источник исключения: cth.Transaction.Commit (); в этом методе Quartz.NET.

/// <summary>
/// Commit the supplied connection.
/// </summary>
/// <param name="cth">The CTH.</param>
/// <param name="openNewTransaction">if set to <c>true</c> opens a new transaction.</param>
/// <throws>JobPersistenceException thrown if a SQLException occurs when the </throws>
protected virtual void CommitConnection(ConnectionAndTransactionHolder cth, bool openNewTransaction)
{
    CheckNotZombied(cth);

    if (cth.Transaction != null)
    {
        try
        {
            IsolationLevel il = cth.Transaction.IsolationLevel;
            cth.Transaction.Commit();
            if (openNewTransaction)
            {
                // open new transaction to go with
                cth.Transaction = cth.Connection.BeginTransaction(il);
            }
        }
        catch (Exception e)
        {
            throw new JobPersistenceException("Couldn't commit ADO.NET transaction. " + e.Message, e);
        }
    }
}

Вот как я создаю ISchedulerFactory:

public static ISchedulerFactory CreateSQLiteSchedFactory(SQLiteConnection sqlConn, string tablePrefix) {
    // db provider hinzufügen
    var metaData = new DbMetadata();
    metaData.AssemblyName = "System.Data.SQLite,Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139";
    metaData.BindByName = true;
    metaData.CommandBuilderType = typeof(SQLiteCommandBuilder);
    metaData.CommandType = typeof(SQLiteCommand);
    metaData.ConnectionType = typeof(SQLiteConnection);
    metaData.ExceptionType = typeof(SQLiteException);
    metaData.ParameterDbType = typeof(TypeAffinity);
    metaData.ParameterDbTypePropertyName = "DbType";
    metaData.ParameterNamePrefix = "@";
    metaData.ParameterType = typeof(SQLiteParameter);
    metaData.UseParameterNamePrefixInParameterCollection = true;
    DbProvider.RegisterDbMetadata("SQLite-1066", metaData);

    // konfiguration für factory erstellen
    NameValueCollection properties = new NameValueCollection();
    properties["quartz.scheduler.instanceName"] = "TestScheduler";
    properties["quartz.scheduler.instanceId"] = "instance_one";
    properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
    properties["quartz.threadPool.threadCount"] = "5";
    properties["quartz.threadPool.threadPriority"] = "Normal";
    properties["quartz.jobStore.misfireThreshold"] = "60000";
    properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
    properties["quartz.jobStore.useProperties"] = "false";
    properties["quartz.jobStore.dataSource"] = "default";
    properties["quartz.jobStore.tablePrefix"] = tablePrefix;
    properties["quartz.jobStore.clustered"] = "true";

    properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";

    properties["quartz.dataSource.default.connectionString"] = sqlConn.ConnectionString;
    properties["quartz.dataSource.default.provider"] = "SQLite-1066";

    // factory erzeugen
    return new StdSchedulerFactory(properties);
}

SQLiteConnection создается со строкой подключения, подобной «Data Source = c: \ mydb.db; Version = 3;» и все кварцевые таблицы инициализируются с помощью поставляемого скрипта SQL


person Rev    schedule 06.12.2012    source источник


Ответы (2)


Вы должны установить для него значение true в свойствах:

properties["quartz.jobStore.txIsolationLevelSerializable"] = "true";
person Claudiu Constantin    schedule 13.10.2016
comment
У вас есть дополнительная информация (источник?), Почему это должно решить проблему? - person Rev; 13.10.2016

Причина этой ошибки, скорее всего, связана с несколькими одновременными записями в базе данных SQLite, sqlite может принимать несколько соединений только для чтения, но не может принимать одновременные записи!

http://www.sqlite.org/faq.html#q5

person Sawan    schedule 06.12.2012
comment
Тогда возникает вопрос, почему при добавлении / удалении заданий происходят какие-либо одновременные записи и что я могу сделать, чтобы минимизировать вероятность. Параллельное выполнение заданий (насколько я могу судить) выполняется отлично. Но да, пока можно предположить, что Quartz не во всех случаях должным образом обрабатывает требования параллелизма записи SQLite. - person Rev; 06.12.2012