C# lucene.net IndexWriter和Azure WebJob

C# lucene.net IndexWriter和Azure WebJob,c#,azure,lucene,lucene.net,azure-webjobs,C#,Azure,Lucene,Lucene.net,Azure Webjobs,我得到了一个持续运行的Azure webjob,它根据队列触发器触发。队列包含需要写入我的lucene索引的项目列表。我目前有很多项目在队列中(超过500k行项目),我正在寻找最有效的方法来处理它。当我尝试“扩展”webjob时,我总是遇到IndexWriter锁异常 当前设置: JobHostConfiguration config = new JobHostConfiguration(); config.Queues.BatchSize = 1;

我得到了一个持续运行的Azure webjob,它根据队列触发器触发。队列包含需要写入我的lucene索引的项目列表。我目前有很多项目在队列中(超过500k行项目),我正在寻找最有效的方法来处理它。当我尝试“扩展”webjob时,我总是遇到IndexWriter锁异常

当前设置:

JobHostConfiguration config = new JobHostConfiguration();
            config.Queues.BatchSize = 1;

            var host = new JobHost(config);                        
            host.RunAndBlock();
网络作业功能

     public static void AddToSearchIndex([QueueTrigger("indexsearchadd")] List<ListingItem> items, TextWriter log)
                {
                    var azureDirectory = new AzureDirectory(CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString), "megadata");
                    var findexExists = IndexReader.IndexExists(azureDirectory);
                    var count = items.Count;
                    IndexWriter indexWriter = null;
                    int errors = 0;
                    while (indexWriter == null && errors < 10)
                    {
                        try
                        {
                            indexWriter = new IndexWriter(azureDirectory, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), !IndexReader.IndexExists(azureDirectory), new Lucene.Net.Index.IndexWriter.MaxFieldLength(IndexWriter.DEFAULT_MAX_FIELD_LENGTH));
                        }
                        catch (LockObtainFailedException)
                        {
                            log.WriteLine("Lock is taken, Hit 'Y' to clear the lock, or anything else to try again");
                            errors++;
                        }
                    };
                    if (errors >= 10)
                    {
                        azureDirectory.ClearLock("write.lock");
                        indexWriter = new IndexWriter(azureDirectory, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), !IndexReader.IndexExists(azureDirectory), new Lucene.Net.Index.IndexWriter.MaxFieldLength(IndexWriter.DEFAULT_MAX_FIELD_LENGTH));
 log.WriteLine("IndexWriter lock obtained, this process has exclusive write access to index");
            indexWriter.SetRAMBufferSizeMB(10.0);
            // Parallel.ForEach(items, (itm) =>
            //{
            foreach (var itm in items)
            {
                AddtoIndex(itm, indexWriter);
            }
            //});
    }
我尝试过的事情:

  • 将azure配置批处理大小设置为最大32
  • 使方法异步并使用Task.WhenAll
  • 使用并行循环
  • 当我尝试上述方法时,通常会失败:

    Lucene.Net.Store.LockObtainFailedException: Lucene.Net.Store.LockObtainFailedException: Lock obtain timed out: AzureLock@write.lock.
     at Lucene.Net.Store.Lock.Obtain(Int64 lockWaitTimeout) in d:\Lucene.Net\FullRepo\trunk\src\core\Store\Lock.cs:line 97
     at Lucene.Net.Index.IndexWriter.Init(Directory d, Analyzer
    
    关于如何在体系结构上设置此web作业,使其能够处理队列中的更多项目,而不是逐个处理,有什么建议吗?它们需要写入相同的索引吗?
    谢谢

    当多个进程试图同时写入Lucene索引时,您遇到了Lucene语义问题。扩展azure应用程序、使用Tasks或parallel for循环只会导致问题,因为当时只有一个进程应该写入Lucene索引

    这是你应该做的

    • 确保在任何时候都只运行一个webjobs实例,即使 如果Web应用程序可缩放(例如,通过自动缩放)
    • 使用最大webjob批量大小(32)
    • 在每个批处理之后提交Lucene索引,以最小化I/O
    通过向webjob项目添加settings.job文件,确保只能执行一个webjob实例。将生成操作设置为内容并复制到输出目录。将以下JSON添加到文件中

    { "is_singleton": true }
    
    将webjob批处理站点配置为最大

    JobHostConfiguration config = new JobHostConfiguration();
    config.Queues.BatchSize = 1;
    var host = new JobHost(config);                        
    host.RunAndBlock();
    
    在每个批处理之后提交Lucene索引

    public static void AddToSearchIndex([QueueTrigger("indexsearchadd")] List<ListingItem> items, TextWriter log)
    {
        ...
        indexWriter = new IndexWriter(azureDirectory, …);
    
        foreach (var itm in items)
        {
            AddtoIndex(itm, indexWriter);
        }
        indexWriter.Commit();
    }
    
    public static void AddToSearchIndex([QueueTrigger(“IndexSearchAddd”)]列表项,TextWriter日志)
    {
    ...
    indexWriter=新的indexWriter(azureDirectory,…);
    foreach(项目中的var itm)
    {
    AddtoIndex(itm、indexWriter);
    }
    indexWriter.Commit();
    }
    
    这只会在提交Lucene索引时写入存储帐户,从而加快索引过程。此外,webjob批处理还将加快消息处理(一段时间内处理的消息数量,而不是单个消息处理时间)

    您可以添加检查以查看Lucene索引是否已锁定(write.lock文件存在),并在批处理开始时解锁索引。这永远不应该发生,但一切都可能发生,所以我想补充一句以确保这一点

    您可以通过使用更大的Web应用程序实例(里程可能有所不同)和使用更快的存储(如Azure Premium存储),进一步加快索引过程


    你可以阅读更多关于这个问题的信息。

    我认为你应该首先把重点放在Lucene上,从而简化你的问题。我不知道Lucene,但您基本上是在寻找与这些Lucene SDK组件一起使用的正确的多并发编写器模式。也就是说,您的问题可以简化为单个exe,多个线程都试图更新索引。一旦你有了有效的工作,这将直接转化为WebJobs。感谢您的详细回复:)稍微改变了我的方法,并获得了更高效的流程,但这肯定会有助于解决我最初的问题。只是出于好奇–您的解决方案最终是如何完成的?我有一项单独的工作,在将项目列表添加到队列之前进行一些预处理触发了这个webjob。相反,我使用相同的作业写入索引,而不是将项目放到队列中,我发现它工作得非常快速有效,没有锁,当然也节省了一些事务成本。我很欣赏您对webjobs的见解,尤其是对单实例配置的见解:)
    public static void AddToSearchIndex([QueueTrigger("indexsearchadd")] List<ListingItem> items, TextWriter log)
    {
        ...
        indexWriter = new IndexWriter(azureDirectory, …);
    
        foreach (var itm in items)
        {
            AddtoIndex(itm, indexWriter);
        }
        indexWriter.Commit();
    }