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; + } + /** * 设置值 *