Quartz.NET + SQLite Jobstore抛出JobPersistenceException

时间:2012-12-06 14:10:50

标签: c# sqlite quartz.net system.data.sqlite

在调度程序中添加或删除作业时,Quartz偶尔抛出JobPersistenceException(在前面的SQLiteException之后)。

看似值得注意的事情:

  • Quartz.NET 2.01 + System.Data.SQLite 1.0.66(两个最新版本在编写时只是注意到有一个SQLite 1.0.82的二进制包可用)
  • 如果当前没有执行作业/触发器,我也会抛出异常(我正在监视Quartz监听器)
  • 从UI上下文手动添加作业(我需要大约10-20次重复才能导致错误,但似乎完全随机)
  • 一切似乎运行正常(多个作业,并行执行,应用程序重启后持久)只要我不触摸AddJob()/ DeleteJob()经过扩展测试后我确定它的与添加/删除作业无关。数据库锁定/访问问题是一个普遍问题。

是否有任何推荐的程序我不知道在添加/删除作业时必须遵循这些程序?

我的ISchedulerFactory配置有什么问题吗? (见下文)

补充

  • 我尝试使用System.Data.SQLite 1.0.82,这让事情变得更糟。一旦Quartz执行一个Job,我就会得到“SQLite错误(5):数据库被锁定”。
  • Quartz.NET将System.Data.SQLite 1.0.56列为受支持的数据库提供程序,因此可能会出现使用较新版本的问题。但是,我不考虑从1.0.66作为选项返回,因为有很多改进/修复IIRC。
  • 我在2.0.1版本修订版(624)和当前头版修订版(669)之间查看了Quartz.NET的开发主干。似乎没有相关的修复。
  • 我怀疑它是一个System.Data.SQLite问题。我偶然发现了几个帖子(涉及不同的SQLite版本),提到内部处理资源可能存在一些问题,导致数据库文件被锁定。

补充2

现在,我放弃了这一点。我尝试了很多东西,但发展必须继续。我切换到另一个数据库类型(Firebird),到目前为止似乎与Quartz一起使用。

如果有人得到这个工作,我很乐意听到它。

-

异常详情:

Quartz.JobPersistenceException:“无法提交ADO.NET事务。数据库文件被锁定\ r \ n数据库被锁定”

堆栈

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 \ n数据库被锁定”

堆栈

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);
}

使用类似于“Data Source = c:\ mydb.db; Version = 3;”的连接字符串创建SQLiteConnection。并使用提供的SQL脚本

初始化所有石英表

2 个答案:

答案 0 :(得分:2)

您必须在属性中将此项设置为true:

properties["quartz.jobStore.txIsolationLevelSerializable"] = "true";

答案 1 :(得分:1)

此错误的原因很可能是因为SQLite数据库上有多个并发写入,sqlite只能接受多个只读连接,但不能同时接受写入!

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