网上开店网站,凡科快图免费下载,深圳网站设计设计,什么视频网站可以做链接作者#xff1a;来自 Elastic Benjamin Trent 有时#xff0c;一行代码需要几天的时间才能写完。在这里#xff0c;我们可以看到工程师在多日内调试代码以修复潜在的 Apache Lucene 索引损坏的痛苦。 做好准备
这篇博客与往常不同。它不是对新功能或教程的解释。这是关于花…作者来自 Elastic Benjamin Trent 有时一行代码需要几天的时间才能写完。在这里我们可以看到工程师在多日内调试代码以修复潜在的 Apache Lucene 索引损坏的痛苦。 做好准备
这篇博客与往常不同。它不是对新功能或教程的解释。这是关于花了三天时间编写的一行代码。我希望你能从中学到一些要点
只要有足够的时间和正确的工具所有不稳定的测试都是可重复的多层测试是稳健系统的关键。但是更高级别的测试变得越来越难以调试和重现。Sleep 是一个出色的调试器 Elasticsearch 如何测试
在 Elastic我们有大量针对 Elasticsearch 代码库的测试。有些是简单而有针对性的功能测试有些是单节点 “快乐路径 - happy path” 集成测试还有一些试图破坏集群以确保在故障情况下一切正常运行。当测试不断失败时工程师或工具自动化将创建一个 github 问题并将其标记为特定团队进行调查。这个特定的错误是由最后一种测试发现的。这些测试很棘手有时只有在多次运行后才能重复。 这个测试实际上在测试什么 这个特定的测试很有趣。它将创建一个特定的映射并将其应用于主分片。然后尝试创建副本。关键的区别在于当副本尝试解析文档时测试会注入异常从而导致恢复以令人惊讶但意料之中的方式失败。
一切都按预期进行但有一个重大问题。在测试清理期间我们验证了一致性并且在那里这个测试遇到了障碍。
这个测试未能以预期的方式失败。在一致性检查期间我们将验证所有复制和主 Lucene 段文件是否一致。意思是未损坏且完全复制。部分数据或损坏的数据比完全失败更糟糕。以下是失败的可怕且简短的堆栈跟踪。
Caused by: org.apache.lucene.index.CorruptIndexException: Problem reading index from store(ByteSizeCachingDirectory(ElasticsearchMockDirectoryWrapper(HybridDirectory/opt/buildkite-agent/builds/bk-agent-prod-gcp-1707109485745743789/elastic/elasticsearch-periodic/server/build/testrun/internalClusterTest/temp/org.elasticsearch.indices.recovery.IndexRecoveryIT_40853F21F419B395-001/tempDir-005/node_t0/indices/ZNwxG7VvShuwYV78RTjknA/0/index lockFactoryorg.apache.lucene.store.NativeFSLockFactory2c169f59))) (resourcestore(ByteSizeCachingDirectory(ElasticsearchMockDirectoryWrapper(HybridDirectory/opt/buildkite-agent/builds/bk-agent-prod-gcp-1707109485745743789/elastic/elasticsearch-periodic/server/build/testrun/internalClusterTest/temp/org.elasticsearch.indices.recovery.IndexRecoveryIT_40853F21F419B395-001/tempDir-005/node_t0/indices/ZNwxG7VvShuwYV78RTjknA/0/index lockFactoryorg.apache.lucene.store.NativeFSLockFactory2c169f59))))at org.apache.lucene.index.SegmentCoreReaders.init(SegmentCoreReaders.java:165)at org.apache.lucene.index.SegmentReader.init(SegmentReader.java:96)at org.apache.lucene.index.ReadersAndUpdates.getReader(ReadersAndUpdates.java:178)at org.apache.lucene.index.ReadersAndUpdates.getLatestReader(ReadersAndUpdates.java:243)at org.apache.lucene.index.SoftDeletesRetentionMergePolicy.keepFullyDeletedSegment(SoftDeletesRetentionMergePolicy.java:82)at org.apache.lucene.index.FilterMergePolicy.keepFullyDeletedSegment(FilterMergePolicy.java:118)at org.apache.lucene.index.FilterMergePolicy.keepFullyDeletedSegment(FilterMergePolicy.java:118)at org.apache.lucene.index.ReadersAndUpdates.keepFullyDeletedSegment(ReadersAndUpdates.java:822)at org.apache.lucene.index.IndexWriter.isFullyDeleted(IndexWriter.java:6078)snipCaused by: java.io.FileNotFoundException: No sub-file with id .kdi found in compound file _0.cfs (fileName_0.kdi files: [_0.pos, .nvm, .fnm, _0.tip, _Lucene90_0.dvd, _0.doc, _0.tim, _Lucene90_0.dvm, _ES87BloomFilter_0.bfm, .fdm, .nvd, _ES87BloomFilter_0.bfi, _0.tmd, .fdx, .fdt])at org.apache.lucene.codecs.lucene90.Lucene90CompoundReader.openInput(Lucene90CompoundReader.java:170)at org.apache.lucene.codecs.lucene90.Lucene90PointsReader.init(Lucene90PointsReader.java:63)at org.apache.lucene.codecs.lucene90.Lucene90PointsFormat.fieldsReader(Lucene90PointsFormat.java:74)at org.apache.lucene.index.SegmentCoreReaders.init(SegmentCoreReaders.java:152)snip不知何故在强制复制失败期间复制的分片最终被损坏了让我用通俗易懂的英语解释一下错误的关键部分。
Lucene 是一种基于段segment的架构这意味着每个段都知道并管理自己的只读文件。这个特定的段正在通过其 SegmentCoreReaders 进行验证以确保一切都是一致的。每个核心读取器都存储了元数据指示给定段存在哪些字段类型和文件。但是在验证 Lucene90PointsFormat 时缺少某些预期文件。对于段 _0.cfs 文件我们期望一个名为 kdi 的点格式文件。cfs 代表 “复合文件系统 - compound file system”Lucene 有时会将所有字段类型和所有小文件组合成一个更大的文件以实现更高效的复制和资源利用。事实上所有三个点文件扩展名kdd、kdi 和 kdm 都丢失了。我们怎么会遇到 Lucene 段期望找到一个点文件但却丢失的情况看起来像是一个可怕的损坏错误 修复每个错误的第一步都是复制它
复制这个特定错误的故障非常痛苦。虽然我们利用了 Elasticsearch 中的随机值测试randomized value testing但我们一定会为每个故障提供一个希望可重现的随机种子以确保可以调查所有故障。好吧除了由竞争条件race condition引起的故障外这对所有故障都非常有用。
./gradlew :server:internalClusterTest --tests org.elasticsearch.indices.recovery.IndexRecoveryIT.testDoNotInfinitelyWaitForMapping -Dtests.seed40853F21F419B395 -Dtests.jvm.argline-Des.concurrent_searchtrue -Dtests.localeid-ID -Dtests.timezoneAsia/Jerusalem -Druntime.java21
无论我尝试多少次特定种子都不会在本地重复失败。但是有办法执行测试并推动更可重复的失败。
我们的特定测试套件允许通过 -Dtests.iters 参数在同一命令中多次运行给定测试。但这还不够我需要确保执行线程正在切换从而增加发生这种竞争条件的可能性。系统的另一个障碍是测试最终运行时间太长测试运行器会超时。最后我使用以下噩梦般的 bash 来重复运行测试
for run in {1..10}; do ./gradlew :server:internalClusterTest --tests org.elasticsearch.indices.recovery.IndexRecoveryIT.testDoNotInfinitelyWaitForMapping -Dtests.jvm.argline-Des.concurrent_searchtrue -Dtests.iters10 ; done || exit 1
压力测试来了。这可以让你快速启动一个进程该进程只会占用 CPU 核心。在运行失败测试的多次迭代时随机发送压力测试终于让我能够复制失败。更近了一步。要对系统施加压力只需打开另一个终端窗口并运行
stress-ng --cpu 16 揭示问题
现在测试失败已经基本可以重复出现是时候尝试找到问题的根源了。这次测试奇怪的地方在于Lucene 抛出了异常原因是它期望有点值point values但测试中并未直接添加任何点值只添加了文本值。这让我开始考虑最近对乐观并发控制字段 _seq_no 和 _primary_term 的更改这两个字段都被索引为点值并存在于每个 Elasticsearch 文档中。
果然有一个提交更改了 _seq_no 的映射器mapper是的这一定是原因但我的兴奋很快被浇灭了。这个更改仅仅调整了字段添加到文档的顺序。在此之前_seq_no 字段是最后添加到文档的之后它们是最先添加的。而字段添加顺序不可能导致 Lucene 文档的这种失败吧……
然而事实证明字段添加顺序的确引发了这个问题。这令人意外最终发现这是 Lucene 本身的一个 Bug解析字段的顺序改变不应该影响解析文档的行为。 Lucene 中的错误
事实上Lucene 中的错误主要集中在以下情况
索引点值字段例如 _seq_no尝试索引文本字段在分析过程中抛出在这种奇怪的状态下我们从遇到文本索引分析异常的写入器打开近实时读取器
但无论我尝试多少种方法都无法完全复制。我直接在整个 Lucene 代码库中添加了暂停点以进行调试。我尝试在异常路径中随机打开读取器。我甚至打印出数兆字节的日志试图找到发生此故障的确切路径。我就是做不到。我花了一整天的时间战斗并失败。
然后我睡着了。
第二天我重新阅读了原始堆栈跟踪并发现了以下行 at org.apache.lucene.index.SoftDeletesRetentionMergePolicy.keepFullyDeletedSegment(SoftDeletesRetentionMergePolicy.java:82)
在我所有的重现尝试中我从未专门设置保留合并策略。Elasticsearch 使用 SoftDeletesRetentionMergePolicy以便我们可以准确地复制副本中的删除并确保我们所有的并发控制都负责实际删除文档的时间。否则 Lucene 将完全控制并会在任何合并时删除它们。
一旦我添加了此策略并复制了上述最基本的步骤故障就会立即复制。
我从来没有像现在这样高兴地在 Lucene 中发现一个 bug。 但这是值得的。 还不是结束
希望你和我一起享受这段疯狂的旅程编写软件尤其是像 Elasticsearch 和 Apache Lucene 这样广泛使用且复杂的软件是值得的。然而有时它非常令人沮丧。我既爱又恨软件。错误修复永远不会结束
Elasticsearch 包含新功能可帮助你为你的用例构建最佳搜索解决方案。深入了解我们的示例笔记本以了解更多信息开始免费云试用或立即在你的本地机器上试用 Elastic。 原文Lucene bug adventures: Fixing a corrupted index exception - Elasticsearch Labs