news 2026/9/15 11:45:45

Quickwit Metastore 本地开发指南:PostgreSQL 与 File-backed 双实现的测试、迁移与演进实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Quickwit Metastore 本地开发指南:PostgreSQL 与 File-backed 双实现的测试、迁移与演进实践

Quickwit Metastore 本地开发指南:PostgreSQL 与 File-backed 双实现的测试、迁移与演进实践

【免费下载链接】quickwitCloud-native OSS search engine for observability项目地址: https://gitcode.com/GitHub_Trending/qu/quickwit

本篇技术指南以 quickwit-metastore 模块 为核心,系统讲解 Quickwit(云原生可观测性搜索引擎)元数据存储层的本地开发全流程:如何用 Docker 快速拉起 PostgreSQL 测试环境、如何分别运行 File-backed 与 PostgreSQL 两套后端的测试、如何使用 sqlx-cli 管理 schema 迁移,以及 deferred migrations(延迟迁移)机制的设计原理与编写规则。读完本文,你将能独立搭建 quickwit-metastore 的开发与测试环境,理解其迁移流水线,并为该模块贡献或调试代码。

一、quickwit-metastore 模块定位

quickwit-metastore是 Quickwit 中用于持久化索引元数据的抽象层。从 crate 根文档 可以看到,它负责屏蔽不同 metastore 实现的差异,目前提供两种后端:

  • File-backed metastore(文件后端):元数据以文件形式存储,适用于单机、开发调试场景;
  • PostgreSQL metastore(数据库后端):官方推荐用于分布式部署场景。

该层维护的元数据包括:索引配置(index configuration)、每个 split 的元信息(ID、文档数、大小、min/max 时间戳、标签集合)、各数据源的 checkpoint,以及索引创建时间等附加信息(详见 metastore 配置文档)。

从源码结构看,MetastoreResolver 负责根据metastore_uri的协议前缀将请求分发到对应工厂:MetastoreFactory 是构建MetastoreServiceClient的 trait 抽象,其中postgres后端只有在编译期开启postgresfeature 时才会注册(见 metastore_resolver.rs),否则会注册一个返回UnsupportedBackend错误的占位实现。理解这一点,就能明白为什么测试命令要区分cargo testcargo test --features=postgres

二、本地启动 PostgreSQL 测试环境

README 提供的第一个命令是启动一个本地 PostgreSQL 服务器,用于测试 postgres metastore 实现:

docker-compose up postgres

该服务定义在仓库根目录的 docker-compose.yml 中,关键配置如下:

配置项
镜像postgres:${POSTGRES_VERSION:-12.17-alpine}(默认锁定最低受支持版本)
端口映射${MAP_HOST_POSTGRES:-127.0.0.1}:5432:5432(默认只绑定本机回环地址)
用户${POSTGRES_USER:-quickwit-dev}
密码${POSTGRES_PASSWORD:-quickwit-dev}
数据库${POSTGRES_DB:-quickwit-metastore-dev}
数据目录PGDATA=/var/lib/postgresql/data/pgdata
数据卷postgres_data:/var/lib/postgresql/data
健康检查pg_isready(1 秒间隔、最多重试 100 次)

docker-compose 文件头部注释(docker-compose.yml)特别说明:关键服务(如 postgres、pulsar)会刻意运行在最老的支持版本上,以验证向后兼容;如需使用最新镜像版本,可修改.env文件覆盖POSTGRES_VERSION等变量,但要注意旧数据卷可能与新版本不兼容。

数据持久化与清理

README 明确指出:PostgreSQL 的数据保存在数据卷(volume)中,两次运行之间不会被自动清理。也就是说,上一次测试遗留的库表会保留下来,这是有意为之——避免每次启动都重新建表。

清理命令在 README 中写作make rm-postgres;需要说明的是,在当前仓库的 Makefile 中,对应的实际目标是docker-rm-postgres-volume,其执行内容为:

docker volume rm quickwit_postgres_data

此外make docker-clean(见 Makefile)也会连同 azurite、fake GCS、grafana、localstack 等开发服务的 volume 一并清理。你可以根据实际仓库的 Makefile 目标选择对应的清理命令。

三、测试 quickwit-metastore:从纯文件后端到 PostgreSQL

3.1 仅测试 FileBackedMetastore

如果只想运行不依赖外部服务的文件后端测试,直接在quickwit项目根目录执行:

cargo test

此命令运行quickwit-metastorecrate 的单元测试,此时postgresfeature 处于关闭状态,测试只覆盖FileBackedMetastore。从 lib.rs 可以看到,测试模式下会通过metastore_for_test()构造一个基于RamStorage(内存存储)的FileBackedMetastore客户端,因此纯文件后端测试完全不需要任何外部进程。

3.2 测试包含 PostgresqlMetastore

要测试 PostgreSQL 后端,需要先启动 PostgreSQL。README 给出的标准做法是在项目根目录使用 Makefile 封装:

make docker-compose-up DOCKER_SERVICES=postgres

docker-compose-up目标(Makefile)会设置COMPOSE_PROFILES=postgres并执行docker compose up -d --remove-orphans --wait,通过 compose profile 只启动postgres这一个服务,并等待其健康检查通过后再返回。

PostgreSQL 就绪后,运行:

cargo test --features=postgres

--features=postgres会启用 Cargo.toml 中定义的 feature,它级联开启quickwit-proto/postgresquickwit-parquet-engine/postgres,并引入sea-querysea-query-bindersqlx等数据库相关依赖。只有开启该 feature,PostgresqlMetastore才会被编译(见 lib.rs),相关集成测试也才会执行。

测试结束后,停止并移除 PostgreSQL 容器:

docker-compose down

从源码看,metastore 的测试覆盖相当全面:tests 目录 下包含index.rssplit.rsdelete_task.rssource.rstemplate.rsshard.rsmetrics.rslist_splits.rsget_identity.rs等测试模块,覆盖了索引生命周期、split 管理、删除任务、源 checkpoint、模板、分片、指标等核心路径;backward_compatibility_tests 则利用 test-data 下的历史版本 JSON 快照(如 v0.7/v0.8/v0.9)验证新旧数据格式的兼容性。

四、sqlx-cli 与迁移工作流

PostgreSQL 后端的 schema 演进由sqlx迁移框架管理。README 建议(但非必需)安装 sqlx-cli 来手工操作迁移:

cargo install sqlx-cli

安装完成后,可以用以下命令**应用(run)或回滚(revert)**常规迁移。迁移源目录为migrations/postgresql,数据库连接串为:

postgres://quickwit-dev:quickwit-dev@localhost:5432/quickwit-metastore-dev
sqlx migrate run --database-url postgres://quickwit-dev:quickwit-dev@localhost:5432/quickwit-metastore-dev --source migrations/postgresql sqlx migrate revert --database-url postgres://quickwit-dev:quickwit-dev@localhost:5432/quickwit-metastore-dev --source migrations/postgresql

这两条命令分别用于在开发数据库上应用最新迁移、或回滚最近一次迁移,适合在修改迁移文件后反复验证。

对于延迟迁移(deferred migrations),则使用另一个迁移源目录:

sqlx migrate run --database-url postgres://quickwit-dev:quickwit-dev@localhost:5432/quickwit-metastore-dev --source migrations/postgresql_deferred

迁移文件组织方式

migrations/postgresql下的迁移采用成对的 up/down 文件组织,目前已有编号 1 至 28 的常规迁移,例如:

  • 1_create-indexes.up.sql/1_create-indexes.down.sql:创建indexes表、update_timestamp触发器函数,并兼容旧版 diesel 迁移(见 1_create-indexes.up.sql);
  • 2_create-splits.up.sql/2_create-splits.down.sql:创建splits表(含split_id主键、split_statetime_range_start/endtags TEXT[]split_metadata_json等字段),并通过触发器在 split 变更时联动更新所属索引的update_timestamp(见 2_create-splits.up.sql);
  • 后续迁移按需为表增加字段(如publish_timestampincarnation_idmaturity_timestampnode_id)、创建新表(delete_taskstemplatesmetrics_splitssketch_splits)或调整主键与唯一索引(如 22/23 号迁移)。

这种编号递增 + up/down 对称的模式,保证了 schema 可以双向演进,也便于 sqlx-cli 追踪当前版本。

五、Deferred migrations:长耗时迁移的优雅降级方案

5.1 为什么需要 deferred migrations

常规迁移要求在启动时同步、快速地完成,因此不适合承载CREATE INDEX CONCURRENTLY这类长时间运行、且不能放在事务块内的 DDL。为此,Quickwit 引入了第二个迁移目录migrations/postgresql_deferred,专门存放这类"长耗时、允许优雅降级"的迁移。其设计约束在 deferred migrations 说明 中有明确记载:

  • 版本号全局唯一:两个目录共享同一张_sqlx_migrations表,因此版本号必须跨目录延续同一条编号序列(当前常规迁移到 28,deferred 从 29 开始);
  • 必须幂等:每条迁移在任何中断场景下都必须可以安全重放;
  • 不能依赖未发布的常规迁移:常规迁移绝不允许依赖 deferred 迁移的结果。

5.2 后台任务 + Postgres advisory lock 的选举机制

deferred migrations 的运行时实现位于 migrator.rs:

  • 常规迁移在run()中同步执行(run_required,见 migrator.rs);
  • deferred 迁移则在就绪(readiness)之后,通过quickwit_common::spawn_named_task派发一个名为postgres_deferred_migrations后台任务执行(见 migrator.rs);
  • 为避免多个 metastore 节点(pod)同时执行长迁移,后台任务先通过SELECT pg_try_advisory_lock($1, $2)竞争一把 Postgres advisory lock,锁键是硬编码的魔数4242421789(见 migrator.rs)。拿到锁的节点成为"迁移领导者"负责执行;拿不到锁的节点打印deferred PostgreSQL migrations handled by another node后直接退出(见 migrator.rs);
  • 关键实现细节:执行前会把连接从连接池detach()出来,让 advisory lock 绑定在单个会话上——PostgreSQL 会在会话结束或连接断开时自动释放该锁,即使迁移执行期间发生 panic 或失败也不会造成锁泄漏(见 migrator.rs)。

执行结果通过指标DEFERRED_MIGRATIONS_APPLY(带success/failure标签)对外暴露,便于运维监控。

5.3 幂等编写规则与真实示例

deferred 迁移的编写有一个硬性要求:凡是不能在事务中执行的语句(如CREATE INDEX CONCURRENTLY),文件首行必须标注-- no-transaction,之后直接写 DDL。由于concurrently模式下每条语句自动提交,迁移中途被 kill 后必须能安全重跑。

以实际的 29 号 deferred 迁移 为例,它为核心压缩器(compaction planner)的扫描路径在splits表上创建一个(maturity_timestamp, split_id)的 B-tree 部分索引:

-- no-transaction CREATE INDEX CONCURRENTLY IF NOT EXISTS splits_maturity_timestamp_idx ON splits (maturity_timestamp, split_id);

该 SQL 文件的注释详细解释了设计考量:planner 每个 tick 都要读取split_state = 'Published'maturity_timestamp > now()的 split 并按时间升序取LIMIT,因此 B-tree 可以让 PostgreSQL 直接按索引顺序 seek 到"尚未成熟"的时间范围,免去额外的排序;split_id作为决胜列保证LIMIT分页的确定性;部分索引谓词必须是 IMMUTABLE,所以不能用now()出现在谓词中,而只能靠maturity_timestamp > now()的查询条件配合。对应的回滚迁移同样以-- no-transaction开头并使用DROP INDEX CONCURRENTLY IF EXISTS(见 29 号 down 迁移)。

deferred 说明文档还提醒了几个运维要点:如果某条迁移执行失败,CREATE INDEX CONCURRENTLY可能留下一个无效(invalid)索引,且不会在迁移表中登记记录,因此需要手工清理后再重试;文档也记录了一个经验教训——曾经尝试在迁移 SQL 内部先清理无效索引,但两条独立语句会被 PostgreSQL 隐式包进一个事务块,反而覆盖了-- no-transaction指令,使concurrently无法工作,因此该方案被放弃。

六、从源码看 metastore 的接入与演进保障

6.1 URI 协议到后端的分发

metastore_uri是 Metastore 的唯一配置入口。在 metastore_resolver.rs 中可以看到协议映射规则:

  • azure://gs://file://ram://s3://File-backed后端(底层复用对象存储);
  • postgres://postgresql://PostgreSQL后端;
  • 其他协议 → 返回UnsupportedBackend错误。

其中postgrespostgresql两种协议前缀均被接受,这一点在 resolver 测试 中有明确断言。测试默认连接串为postgres://quickwit-dev:quickwit-dev@localhost/quickwit-metastore-dev,可通过环境变量QW_TEST_DATABASE_URL覆盖。

另外,resolver 还提供了resolve_read_only()方法用于解析只读连接(适用于从库/读副本场景),该能力仅 PostgreSQL 后端支持——若对 file 后端调用会直接报错,且只读连接上执行写操作(如create_index)会收到Forbidden错误(见 resolver 测试)。

6.2 生产配置要点(补充参考)

虽然本指南聚焦开发测试,但理解生产配置有助于反向理解测试环境的默认值。根据 metastore 配置文档:

  • PostgreSQL URI 格式为postgres://[user]:[password]@[host]:[port]/[dbname],部分参数可省略,但数据库必须预先创建;Quickwit 首次启动时会自动建表,升级时会在启动阶段自动执行迁移;
  • 每个运行 metastore 服务的节点维护独立连接池,默认metastore.postgres.max_connections为 10,因此节点最多承载2 * max_connections = 20个在途请求;排池时需保证metastore_nodes * max_connections低于 PostgreSQL 连接数上限;
  • file-backed 后端每个索引一个元数据文件,路径为[storage_uri]/[index_id]/metastore.json,可通过#polling_interval=30sURI 片段开启轮询刷新(仅支持秒);注意 file-backed 后端没有锁机制,同一时刻只允许一个实例运行。

七、开发自检清单

完成阅读后,建议按以下清单快速自检环境:

  1. docker-compose up postgres可正常拉起 PostgreSQL,容器健康检查通过;
  2. 未启动数据库时cargo test全绿(只覆盖 File-backed 后端);
  3. 启动数据库后make docker-compose-up DOCKER_SERVICES=postgres再跑cargo test --features=postgres全绿(覆盖 PostgreSQL 后端);
  4. 新增迁移时:常规迁移放在migrations/postgresql,长耗时/不可事务化的 DDL 放在migrations/postgresql_deferred,编号全局递增、up/down 成对、-- no-transaction正确标注、语句幂等;
  5. 用 sqlx-cli 对本地库反复run/revert验证迁移可双向执行;
  6. 结束时docker-compose down,如需彻底重置数据卷再执行make docker-rm-postgres-volume(README 中写作make rm-postgres)。

通过上述流程,你可以在不污染任何共享环境的前提下,独立完成 quickwit-metastore 的测试、迁移开发与调试,并为该模块的后续演进打下坚实基础。

【免费下载链接】quickwitCloud-native OSS search engine for observability项目地址: https://gitcode.com/GitHub_Trending/qu/quickwit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 11:44:13

AI如何通过NLP技术革新毕业论文写作全流程

1. 项目概述:AI如何重塑毕业论文写作体验第一次接触"书匠策AI"这个工具时,我正在指导一位大四学生的毕业论文。那个深夜,学生发来第7稿修改文档,文档里密密麻麻的批注和反复修改的段落让我突然意识到:传统论…

作者头像 李华
网站建设 2026/9/15 11:44:10

OpenCV滑块验证码识别与模拟拖动:从图像处理到自动化实战

直接开始写正文,以下就是这篇文章的完整内容。各位做Web自动化、爬虫、RPA的朋友,应该都对滑块验证码不陌生。它几乎是目前互联网上最常见的反自动化手段,形式也五花八门:有的是拖动拼图,有的是按住完成旋转&#xff0…

作者头像 李华
网站建设 2026/9/15 11:43:46

斯威士兰口岸强制型式审批新政正式落地

政策背景2026 年 4 月,斯威士兰通信委员会(ESCCOM)发布第 4/2026 号一般通知,宣布自 2026 年 4 月 13 日(周一)起,对所有进口或在斯威士兰境内使用的电子通信设备,强制要求提供型式审…

作者头像 李华