From 8bb169b4d0b1a02056f3f2bbf53fbaa23883e526 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 1 Oct 2026 10:02:13 +0000 Subject: [PATCH] =?UTF-8?q?fix(manticore):=20=E5=90=8C=E6=AD=A5=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E8=AF=BB=E5=A2=9E=E9=87=8F=E6=8C=87=E9=92=88=E6=94=B9?= =?UTF-8?q?=E7=94=A8=E4=B8=A5=E6=A0=BC=E8=AF=BB=E5=8F=96=EF=BC=8C=E5=BC=95?= =?UTF-8?q?=E6=93=8E=E4=B8=8D=E5=8F=AF=E8=BE=BE=E6=97=B6=E7=BB=88=E6=AD=A2?= =?UTF-8?q?=E8=80=8C=E9=9D=9E=E4=BB=8E=E5=A4=B4=E9=87=8D=E7=81=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ManticoreKeyValue::get() 在引擎重启/不可达时返回默认值 0,导致增量同步从头重灌 并把指针写回低值,引发 AI 插件向量化风暴。新增 getStrict()/queryOneStrict(), 读取失败抛异常,五个同步命令据此终止本轮。 --- app/Console/Commands/SyncFileToManticore.php | 8 ++++- app/Console/Commands/SyncMsgToManticore.php | 8 ++++- .../Commands/SyncProjectToManticore.php | 8 ++++- app/Console/Commands/SyncTaskToManticore.php | 8 ++++- app/Console/Commands/SyncUserToManticore.php | 8 ++++- app/Module/Manticore/ManticoreBase.php | 31 +++++++++++++++++++ app/Module/Manticore/ManticoreKeyValue.php | 25 +++++++++++++++ 7 files changed, 91 insertions(+), 5 deletions(-) diff --git a/app/Console/Commands/SyncFileToManticore.php b/app/Console/Commands/SyncFileToManticore.php index 25bd8d1ba..08b524938 100644 --- a/app/Console/Commands/SyncFileToManticore.php +++ b/app/Console/Commands/SyncFileToManticore.php @@ -74,7 +74,13 @@ class SyncFileToManticore extends Command do { $round++; - $lastId = $isIncremental ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; + try { + // 严格读取:读失败(引擎重启/不可达)不能当成 0,否则会从头重灌并把指针写回低值 + $lastId = $isIncremental ? intval(ManticoreKeyValue::getStrict($lastKey)) : 0; + } catch (\RuntimeException $e) { + $this->error("读取同步指针失败,本轮终止(避免从头重灌):" . $e->getMessage()); + break; + } if ($round === 1) { if ($lastId > 0) { diff --git a/app/Console/Commands/SyncMsgToManticore.php b/app/Console/Commands/SyncMsgToManticore.php index c9947586c..e9a046dde 100644 --- a/app/Console/Commands/SyncMsgToManticore.php +++ b/app/Console/Commands/SyncMsgToManticore.php @@ -81,7 +81,13 @@ class SyncMsgToManticore extends Command // 持续处理循环(增量模式下) do { $round++; - $lastId = $isIncremental ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; + try { + // 严格读取:读失败(引擎重启/不可达)不能当成 0,否则会从头重灌并把指针写回低值 + $lastId = $isIncremental ? intval(ManticoreKeyValue::getStrict($lastKey)) : 0; + } catch (\RuntimeException $e) { + $this->error("读取同步指针失败,本轮终止(避免从头重灌):" . $e->getMessage()); + break; + } if ($round === 1) { if ($lastId > 0) { diff --git a/app/Console/Commands/SyncProjectToManticore.php b/app/Console/Commands/SyncProjectToManticore.php index e8662f4d3..dd5179343 100644 --- a/app/Console/Commands/SyncProjectToManticore.php +++ b/app/Console/Commands/SyncProjectToManticore.php @@ -68,7 +68,13 @@ class SyncProjectToManticore extends Command do { $round++; - $lastId = $isIncremental ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; + try { + // 严格读取:读失败(引擎重启/不可达)不能当成 0,否则会从头重灌并把指针写回低值 + $lastId = $isIncremental ? intval(ManticoreKeyValue::getStrict($lastKey)) : 0; + } catch (\RuntimeException $e) { + $this->error("读取同步指针失败,本轮终止(避免从头重灌):" . $e->getMessage()); + break; + } if ($round === 1) { if ($lastId > 0) { diff --git a/app/Console/Commands/SyncTaskToManticore.php b/app/Console/Commands/SyncTaskToManticore.php index b319fb76e..2b64af233 100644 --- a/app/Console/Commands/SyncTaskToManticore.php +++ b/app/Console/Commands/SyncTaskToManticore.php @@ -68,7 +68,13 @@ class SyncTaskToManticore extends Command do { $round++; - $lastId = $isIncremental ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; + try { + // 严格读取:读失败(引擎重启/不可达)不能当成 0,否则会从头重灌并把指针写回低值 + $lastId = $isIncremental ? intval(ManticoreKeyValue::getStrict($lastKey)) : 0; + } catch (\RuntimeException $e) { + $this->error("读取同步指针失败,本轮终止(避免从头重灌):" . $e->getMessage()); + break; + } if ($round === 1) { if ($lastId > 0) { diff --git a/app/Console/Commands/SyncUserToManticore.php b/app/Console/Commands/SyncUserToManticore.php index 2f199dddf..aee068753 100644 --- a/app/Console/Commands/SyncUserToManticore.php +++ b/app/Console/Commands/SyncUserToManticore.php @@ -68,7 +68,13 @@ class SyncUserToManticore extends Command do { $round++; - $lastId = $isIncremental ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; + try { + // 严格读取:读失败(引擎重启/不可达)不能当成 0,否则会从头重灌并把指针写回低值 + $lastId = $isIncremental ? intval(ManticoreKeyValue::getStrict($lastKey)) : 0; + } catch (\RuntimeException $e) { + $this->error("读取同步指针失败,本轮终止(避免从头重灌):" . $e->getMessage()); + break; + } if ($round === 1) { if ($lastId > 0) { diff --git a/app/Module/Manticore/ManticoreBase.php b/app/Module/Manticore/ManticoreBase.php index 8b91c3588..fcf602807 100644 --- a/app/Module/Manticore/ManticoreBase.php +++ b/app/Module/Manticore/ManticoreBase.php @@ -598,6 +598,37 @@ class ManticoreBase ); } + /** + * 查询单行(严格版):连接/查询失败抛异常,而不是和「没有这一行」一样返回 null + * + * 用于「读不到就当默认值」会造成后果的场景,如同步命令读增量指针—— + * 引擎不可达时若当成指针 0,会从头重灌全部数据。 + * + * @param string $sql SQL语句 + * @param array $params 参数 + * @return array|null 单行结果;没有匹配行返回 null + * @throws \RuntimeException 连接或查询失败 + */ + public function queryOneStrict(string $sql, array $params = []): ?array + { + $failed = new \stdClass(); + $result = $this->runWithRetry( + function (PDO $pdo) use ($sql, $params) { + $stmt = $pdo->prepare($sql); + $this->bindParams($stmt, $params); + $stmt->execute(); + $row = $stmt->fetch(); + return $row ? $this->convertNumericTypesRow($row) : null; + }, + $failed, + ['sql' => $sql, 'params' => $params] + ); + if ($result === $failed) { + throw new \RuntimeException('Manticore 不可达或查询失败'); + } + return $result; + } + /** * 转换结果集中的数值类型 * PDO 默认将 BIGINT 等数值类型返回为字符串,这里统一转换 diff --git a/app/Module/Manticore/ManticoreKeyValue.php b/app/Module/Manticore/ManticoreKeyValue.php index c9ec85499..bcf0cc867 100644 --- a/app/Module/Manticore/ManticoreKeyValue.php +++ b/app/Module/Manticore/ManticoreKeyValue.php @@ -34,6 +34,31 @@ class ManticoreKeyValue return $result ? $result['v'] : $default; } + /** + * 严格获取值:键不存在返回 null,引擎不可达/查询失败抛异常 + * + * get() 对「键不存在」和「读取失败」都返回默认值,无法区分。同步命令用它读增量指针: + * 引擎重启期间读失败若当成 0,会从头重灌全部数据并把指针写回低值。 + * + * @param string $key 键 + * @return string|null 值;键不存在返回 null + * @throws \RuntimeException 引擎不可达或查询失败 + */ + public static function getStrict(string $key): ?string + { + if (!Apps::isInstalled("search")) { + throw new \RuntimeException('Manticore Search 未安装'); + } + + $instance = new ManticoreBase(); + $result = $instance->queryOneStrict( + "SELECT v FROM key_values WHERE k = ?", + [$key] + ); + + return $result ? (string) $result['v'] : null; + } + /** * 设置值 *