Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 171 · После BulkWrite и основ read/write concern; диагностика разобрана по шагам

MongoDB: подтверждение записи, реальные ошибки и диагностика результата

Почему exception не равен откату: сервер записи, WriteConcernError, сверка по ID, ошибки команды, метки и перенос старого WriteException.

Что произошло с данными, если пришло исключение

Вы нажали «Подготовить заказ», сервер отправил изменение в MongoDB, а приложение получило исключение. Можно ли написать пользователю «ничего не изменилось» и повторить действие? Не всегда: сама запись и подтверждение нужного уровня — разные шаги. Здесь получим понятные квитанции и сверим их с настоящими документами. После примеров будет видно, почему конфликт ID, недостаточное подтверждение и ошибка команды требуют разных решений.

Запись и её подтверждение — два разных наблюдения
  1. Приложение: order-17, status draft → ready
  2. Primary применяет изменение документа
  3. Сервер пытается выполнить запрошенный write concern
  4. PHP получает WriteResult или исключение с результатом → отдельно читает status

Primary — узел replica set, который принимает записи. Write concern — требование к подтверждению записи, например от одного узла или большинства. WriteResult похож на квитанцию: содержит счётчики и сведения об ошибках. Аналогия ограничена: квитанция не является новым чтением документа и не описывает все возможные будущие сбои.

$concern = $result->getWriteConcernError();
if ($concern === null) {
    echo 'Ошибка подтверждения не указана';
}

В этом небольшом фрагменте $result — уже полученный WriteResult. $concern сохраняет результат метода; -> вызывает метод объекта; пустые круглые скобки означают отсутствие аргументов. null означает отсутствие объекта ошибки. === сравнивает значение и тип. При нормальном первом примере вывод будет «Ошибка подтверждения не указана». Это не полная проверка успеха: ниже дополнительно разобраны ошибки элементов, счётчики и фактическое состояние.

Подготовка: коллекция и первый запрос, BulkWrite и executeBulkWrite, объекты и методы, подтверждение и чтение. Полные ZIP включают подключение и исходные данные; короткие фрагменты страницы выделяют текущий шаг.

Четыре вопроса к результату вместо общего «ошибка»

Что проверяемГде это находитсяНаблюдение на стендеЧего нельзя заключать
Ошибка одного порученияWriteResult::getWriteErrorsDuplicate11000; existing не заменёнЧто любая ошибка откатывает весь bulk
Уровень подтвержденияWriteResult::getWriteConcernErrorw2 → код100, запись уже виднаЧто isAcknowledged=true доказывает w2
Ошибка командыCommandException::getResultDocumentНеизвестная команда →59/CommandNotFoundЧто у неё есть WriteResult
Признак специальной ситуацииRuntimeException::hasErrorLabelУ выбранных ошибок метки отсутствуютЧто false значит «точно ничего не записано»

WriteError относится к поручению, WriteConcernError — к подтверждению. BulkWriteException сохраняет WriteResult даже при неуспешном выполнении. CommandException содержит другой объект — документ результата команды. Getter читает уже полученные сведения и сам не делает повторный запрос. При чтении документа из базы выполняется отдельная ограниченная операция.

Важная граница: wtimeout ограничивает ожидание нужного подтверждения репликации после применения операции на primary. Само истечение такого ожидания не откатывает уже сделанное. На нашем одноузловом стенде показана ещё более конкретная ситуация — w=2 невозможно удовлетворить, код100. Наличие wtimeout=250 в параметрах не означает, что выполнен опыт с timeout64. Официальные правила write concern.

Запуск на отдельной одноузловой реплике

Для этих девяти программ нужен отдельный одноузловой replica set phpmanRs. На standalone w2 ведёт себя иначе: нельзя подменять им стенд и ожидать те же таблицы. Проверяемая среда: PHP8.5.11 x64, ext-mongodb2.5.5, MongoDB8.0.34, единственный loopback-узел localhost:27076. Программы требуют PHP CLI8.4+ и расширение2.5+, Composer не используют. README каждого архива содержит установку расширения, запуск нового dbpath и однократную инициализацию учебной реплики.

php --ri mongodb
$env:PHPMAN_MONGODB_URI='mongodb://127.0.0.1:27076/?replicaSet=phpmanRs&retryWrites=false'
$env:PHPMAN_MONGODB_DATABASE='php_manual_txn_lab'
php example.php

ZIP содержит example.php и mw129-helper.php. После запуска откройте result.html: там видны строки результата, выбранные технические сведения и версии. result.json содержит те же строки; metrics.json — проверки и контрольные суммы. Helper проверяет единственный loopback-узел, создаёт случайную собственную коллекцию, в finally удаляет только её и подтверждает удаление. Принудительное завершение процесса может прервать очистку.

Адрес сервера берётся из фактического WriteResult: replica set объявляет localhost, хотя seed URI использует127.0.0.1. Это нормальное различие имён. Для рабочего сервера требуются собственные правила доступа, аутентификация и эксплуатационная конфигурация; лабораторный URI не публикуют как рекомендацию открытого сервиса.

Что изменилось в исключениях ext-mongodb2.x

В ext-mongodb2.0 удалён старый абстрактный WriteException. BulkWriteException теперь наследует ServerException. Поэтому старый catch WriteException не является современным обработчиком bulk-ошибки. В2.3 добавлены readonly-свойства writeResult, resultDocument и code/message/info у соответствующих классов; getter остаются доступны.

catch (MongoDB\Driver\Exception\BulkWriteException $error) {
    $result = $error->getWriteResult();
}

Это фрагмент после try вокруг executeBulkWrite, а не самостоятельный PHP-файл. Далее полная программа ловит настоящий duplicate на2.5.5 и показывает class_exists старого имени=false. Историческая карточка WriteException::getWriteResult сохраняет описание API и путь переноса; вызов удалённого метода не объявляется выполненным.

Получить квитанцию смены статуса и узнать выполнивший сервер

Сотрудник перевёл заказ из черновика в готовый. Экрану нужен новый статус, а диагностике — квитанция: был ли найден документ, изменился ли он, какой сервер выполнил запись. Начнём с нормального исхода, чтобы затем не смешивать его с разными ошибками.

Входные данные и понятия

В собственной коллекции лежит order-17 со status=draft. BulkWrite содержит одну команду $set status=ready. WriteConcern(1,250) запрашивает подтверждение одного узла; число 250 — миллисекунды wtimeout, а не количество документов. При w=1 этот timeout ожидания репликации не применяется.

Путь операции и результата
  1. order-17: draft → BulkWrite update $set ready
  2. executeBulkWrite(w=1) → WriteResult
  3. getServer → фактически выполнивший сервер; getWriteConcernError → null
  4. Отдельный find с проекцией status → ready в таблице

Минимальная запись

$result = $manager->executeBulkWrite($namespace, $bulk, [
    'writeConcern' => new MongoDB\Driver\WriteConcern(1, 250),
]);
$server = $result->getServer();
$concern = $result->getWriteConcernError(); // null в этом успешном случае

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

ЗаказПрочитанный статусMatchedModifiedНет concern errorСервер
order-17ready11trueRS primary

Почему получился такой результат

WriteResult — PHP-объект с итогами выполненной операции. getMatchedCount=1 означает совпавший документ, getModifiedCount=1 — действительно изменённый. -> вызывает метод объекта; присваивание $server сохраняет полученный Server. getServer не выбирает новый узел: он связан с этой квитанцией. В подробностях результата видны localhost:27076 и тип RS primary. Конфиденциальное учебное поле customer_secret не запрашивается.

Границы и типичные ошибки

Нет concern error — только один проверенный слой. Здесь также нет ошибок отдельных операций и подтверждён результат чтением. Ноль modified не всегда ошибка: документ мог уже иметь нужное значение. Нормальный w=1 не обещает требуемую приложению многокопийную сохранность; политика подтверждения должна соответствовать задаче и топологии.

Полная самостоятельная программа и архив
Получить квитанцию смены статуса и узнать выполнивший серверСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'order-17','status'=>'draft','customer_secret'=>'hidden']]);
    $bulk=new MongoDB\Driver\BulkWrite;$bulk->update(['_id'=>'order-17'],['$set'=>['status'=>'ready']],['multi'=>false,'upsert'=>false]);
    $receipt=$manager->executeBulkWrite($namespace,$bulk,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,250)]);
    $server=$receipt->getServer();$saved=mw129Find($manager,$namespace,['_id'=>'order-17'],['_id'=>0,'status'=>1])[0];
    $checks=['one_match'=>$receipt->getMatchedCount()===1,'one_change'=>$receipt->getModifiedCount()===1,'no_concern_error'=>$receipt->getWriteConcernError()===null,'no_write_errors'=>$receipt->getWriteErrors()===[],'stored_ready'=>$saved['status']==='ready','server_is_primary'=>$server->getType()===MongoDB\Driver\Server::TYPE_RS_PRIMARY];
    $details=['acknowledged'=>$receipt->isAcknowledged(),'server'=>['host'=>$server->getHost(),'port'=>$server->getPort(),'type'=>$server->getType()],'projection'=>['status'],'write_concern'=>['w'=>1,'wtimeout'=>250]];
    return [['order-17',$saved['status'],$receipt->getMatchedCount(),$receipt->getModifiedCount(),$receipt->getWriteConcernError()===null,'RS primary']];
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-receipt-server',['Заказ', 'Прочитанный статус', 'Matched', 'Modified', 'Нет concern error', 'Сервер'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-receipt-server: OK
';
Ожидаемый результат
mongodb129-write-receipt-server: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Замените исходный status на ready. Matched останется 1, modified станет 0, а чтение вернёт ready. Измените ожидаемый результат и подпись так, чтобы экран не называл это аварией.

Увидеть применённое изменение при ошибке подтверждения

Склад увеличил число резервов на один, но вместо обычного результата получил исключение. Если обработчик сразу повторит $inc, можно зарезервировать лишнюю единицу. Посмотрим на фактическое состояние, не делая второго изменения.

Входные данные и понятия

В документе stock-A reserved=5. Отправляется один $inc reserved:1 с требованием w=2. Наш набор имеет только один узел: два подтверждения получить невозможно. MongoDB8.0.34 возвращает код 100 и текст Not enough data-bearing nodes. Это ошибка недостижимого уровня подтверждения, а не смоделированный таймаут 64.

Путь операции и результата
  1. reserved=5 → один $inc +1
  2. Primary применяет изменение → reserved=6
  3. Запрошено w=2 при одном узле → BulkWriteException
  4. getWriteResult → concern100; find показывает6; повтор не отправлен

Минимальная запись

try {
    $result = $manager->executeBulkWrite($namespace, $bulk, [
        'writeConcern'=>new MongoDB\Driver\WriteConcern(2,250),
    ]);
} catch (MongoDB\Driver\Exception\BulkWriteException $error) {
    $result = $error->getWriteResult();
    $concern = $result->getWriteConcernError();
    // Проверить результат и сохранённую запись; не повторять $inc вслепую.
}

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

НаблюдениеЗначение
До записи5
После $inc и исключения6
Код concern100
WriteResult isAcknowledgedtrue
Повторный $incfalse

Почему получился такой результат

Исключение означает нарушение условия выполнения, но не автоматическое отсутствие любого результата. В его WriteResult modified=1, getWriteErrors пуст, getWriteConcernError содержит ошибку подтверждения. isAcknowledged=true говорит, что операция была с подтверждением и есть соответствующий результат; этот флаг не доказывает удовлетворение w=2. Число 6 получено реальным чтением, а не вычислено для рисунка.

Границы и типичные ошибки

Локальное чтение не заменяет недостающее подтверждение второй копии и не доказывает будущую сохранность при сбое. Здесь известна конкретная ошибка конфигурации. При сети или failover результат может быть неопределённым и потребуется отдельный протокол сверки. Нельзя универсально превращать любой exception в «не записано» или «успешно»: разберите тип и сведения о результате.

Полная самостоятельная программа и архив
Увидеть применённое изменение при ошибке подтвержденияСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'stock-A','reserved'=>5]]);
    $bulk=new MongoDB\Driver\BulkWrite;$bulk->update(['_id'=>'stock-A'],['$inc'=>['reserved'=>1]],['multi'=>false,'upsert'=>false]);
    [$result,$error]=mw129Attempt($manager,$namespace,$bulk,2);$concern=mw129RequireConcern($result,$error,100);
    $saved=mw129Find($manager,$namespace,['_id'=>'stock-A'],['_id'=>0,'reserved'=>1])[0];
    $checks=['applied_once'=>$saved['reserved']===6,'modified_one'=>$result->getModifiedCount()===1,'acknowledged_response'=>$result->isAcknowledged(),'no_item_errors'=>$result->getWriteErrors()===[],'concern_100'=>$concern->getCode()===100,'no_automatic_replay'=>true];
    $details=['requested_w'=>2,'available_members'=>1,'concern_message'=>$concern->getMessage(),'concern_info'=>$concern->getInfo(),'retry_performed'=>false];
    return [['До записи',5],['После $inc и исключения',$saved['reserved']],['Код concern',$concern->getCode()],['WriteResult isAcknowledged',$result->isAcknowledged()],['Повторный $inc',false]];
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-applied-unconfirmed',['Наблюдение', 'Значение'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-applied-unconfirmed: OK
';
Ожидаемый результат
mongodb129-write-applied-unconfirmed: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Измените учебный прирост на 2: сохранённое значение должно стать 7 после единственной отправки. Объясните, почему повтор того же $inc дал бы 9, а повторное чтение оставляет 7.

Вопрос из практики: Mongo DB Write timeout. Автор хочет считать истечение срока доказательством, что запись не состоялась. Вопрос раскрывает смешение применения операции и получения требуемого подтверждения. Обсуждение использовано как постановка задачи; приведённые здесь программы написаны заново по официальным современным контрактам.

Разобрать запрошенное подтверждение из структурированных данных ошибки

Оператору нужно понять, что именно запросило приложение: слишком много копий или несуществующий именованный режим подтверждения. Чтение целого текста ошибки неудобно для отчёта, а разбор английского сообщения регулярным выражением ненадёжен. Возьмём структурированные поля.

Входные данные и понятия

Две независимые вставки запрашивают w=2 и w="missingTag". Такого именованного режима в учебной реплике нет. Обе используют wtimeout=250. getInfo возвращает дополнительный документ; в этом серверном ответе внутри writeConcern находятся w, wtimeout и provenance.

Путь операции и результата
  1. Запрос w=2 или missingTag → реальная вставка
  2. BulkWriteException → WriteResult → WriteConcernError
  3. getCode/getMessage/getInfo → число, текст, объект metadata
  4. Таблица показывает коды100/79, исходный w, 250ms и clientSupplied

Минимальная запись

$info = $concern->getInfo(); // ?object, иногда null
$requested = $info->writeConcern ?? null;
$code = $concern->getCode();
$message = $concern->getMessage();
// Сначала проверить форму metadata; текст сообщения не разбирать регулярным выражением.

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

КодЗапрошенный wwtimeout, мсИсточникInserted
1002250clientSupplied1
79missingTag250clientSupplied1

Почему получился такой результат

Код 100 относится к недостаточному числу узлов, 79 — к неизвестному режиму. clientSupplied указывает, что это значение подтверждения пришло от клиента. Inserted=1 и последующее чтение показывают, что обе записи появились. Наличие поля wtimeout=250 не делает ошибку таймаутом. В ext2.3 появились readonly code/message/info; пример сравнивает их с getter, не изменяя объект ошибки.

Границы и типичные ошибки

Контракт getInfo — object|null, а не фиксированный набор этих трёх ключей на любом сервере. Полная программа проверяет наличие объекта writeConcern перед чтением; прикладной отчёт должен уметь показать отсутствие дополнительных сведений. Код и слой ошибки надёжнее текста для ветвления, но конкретные коды всё равно сверяют с документацией нужной версии. Не исправляйте проблему молчаливым снижением w.

Полная самостоятельная программа и архив
Разобрать запрошенное подтверждение из структурированных данных ошибкиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
$rows=[];$allSame=true;$seen=[];
    foreach([2,'missingTag'] as $i=>$w){$bulk=new MongoDB\Driver\BulkWrite;$bulk->insert(['_id'=>$i+1,'operation'=>'record-'.$i]);
        [$result,$error]=mw129Attempt($manager,$namespace,$bulk,$w);$concern=mw129RequireConcern($result,$error,$i===0?100:79);
        $info=$concern->getInfo();$spec=$info->writeConcern??null;
        if(!is_object($spec)){throw new RuntimeException('Expected writeConcern metadata');}
        $allSame=$allSame && $concern->code===$concern->getCode() && $concern->message===$concern->getMessage() && $concern->info==$info;
        $rows[]=[$concern->getCode(),$spec->w,$spec->wtimeout,$spec->provenance,$result->getInsertedCount()];
        $seen[]=['code'=>$concern->getCode(),'message'=>$concern->getMessage(),'info'=>$info];
    }
    $saved=mw129Find($manager,$namespace,[],['_id'=>1,'operation'=>1]);
    $checks=['two_records_stored'=>count($saved)===2,'numeric_policy_recorded'=>$rows[0][1]===2,'named_policy_recorded'=>$rows[1][1]==='missingTag','bounded_wait_recorded'=>$rows[0][2]===250 && $rows[1][2]===250,'client_provenance'=>$rows[0][3]==='clientSupplied' && $rows[1][3]==='clientSupplied','readonly_matches_getters'=>$allSame];
    $details=['actual_errors'=>$seen,'stored_documents'=>$saved,'error_layer'=>'write concern'];return $rows;
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-concern-metadata',['Код', 'Запрошенный w', 'wtimeout, мс', 'Источник', 'Inserted'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-concern-metadata: OK
';
Ожидаемый результат
mongodb129-write-concern-metadata: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Добавьте колонку «Параметры неизвестны», если getInfo вернул null. Не заполняйте отсутствующие поля вымышленным w=1. Сохраните исходный код и понятное сообщение о нехватке сведений.

Вопрос из практики: php mongodb on BulkWriteException get Additional information. Разработчик ищет сведения об ошибке в getServer()->getInfo и видит не то, что нужно; важно выбрать объект соответствующего слоя результата. Обсуждение использовано как постановка задачи; приведённые здесь программы написаны заново по официальным современным контрактам.

Сверить запись по ID операции перед решением о повторе

Сервис отправки получил ошибку подтверждения при создании записи об упаковке заказа. Ему нужно решить, отправлять ли тот же бизнес-запрос заново. Заранее известный ID операции позволяет сначала найти именно её и сравнить содержимое.

Входные данные и понятия

ID shipment-2024-17 выбран до записи. В документ входят order=order-17 и status=packed. После вставки с w=2 программа читает только _id/order/status этого ID, а затем сверяет оба ожидаемых значения. Коллекция вымышленная и используется только одним процессом опыта.

Путь операции и результата
  1. Заранее выбран shipment-2024-17 + ожидаемые order/status
  2. Одна вставка → concern100, inserted1
  3. Find по точному ID → сравнить order и packed
  4. Запись наблюдается → повтор не отправлен; требование w2 всё ещё не подтверждено

Минимальная запись

$found = mw129Find($manager, $namespace, ['_id'=>$operationId], [
    '_id'=>1, 'order'=>1, 'status'=>1,
]);
// Сверить ID и ожидаемые значения. Наличие на primary не доказывает подтверждение w=2.

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

ID операцииПрочитанный статусРешениеw=2 подтверждён
shipment-2024-17packedЗапись наблюдается; повтор не отправленfalse

Почему получился такой результат

Сверка отличается от поиска «последнего документа»: ID связывает запрос и наблюдаемый результат. Одного совпавшего ID недостаточно, поэтому проверяется содержимое. Если ID найден с другим содержимым, это конфликт, а не успех. В итоговой таблице отдельно стоит false для подтверждения w=2; факт чтения не превращён в более сильную гарантию.

Границы и типичные ошибки

Это демонстрация наблюдения и осторожного решения, а не универсальная exactly-once схема. В распределённой системе нужны правила уникальности ID, повторов, хранения статуса и выбранные read/write concern. Параллельный процесс может изменить запись между шагами; наш отдельный стенд такого соревнования не моделирует. Отсутствие записи при одном чтении также не всегда доказывает, что запрос нигде не применился.

Полная самостоятельная программа и архив
Сверить запись по ID операции перед решением о повтореСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
$operation='shipment-2024-17';$payload=['order'=>'order-17','status'=>'packed'];
    $bulk=new MongoDB\Driver\BulkWrite;$bulk->insert(['_id'=>$operation]+$payload);
    [$result,$error]=mw129Attempt($manager,$namespace,$bulk,2);$concern=mw129RequireConcern($result,$error,100);
    $found=mw129Find($manager,$namespace,['_id'=>$operation],['_id'=>1,'order'=>1,'status'=>1]);
    $same=count($found)===1 && $found[0]['order']===$payload['order'] && $found[0]['status']===$payload['status'];
    $decision=$same?'Запись наблюдается; повтор не отправлен':'Нужна отдельная проверка';
    $checks=['known_id_found'=>count($found)===1,'payload_matches'=>$same,'one_insert_reported'=>$result->getInsertedCount()===1,'requested_concern_not_met'=>$concern->getCode()===100,'still_one_document'=>count(mw129Find($manager,$namespace,[],['_id'=>1]))===1];
    $details=['operation_id'=>$operation,'expected_payload'=>$payload,'observed'=>$found,'required_w'=>2,'confirmed_w2'=>false,'retry_performed'=>false];
    return [[$operation,$found[0]['status'],$decision,false]];
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-reconcile-operation',['ID операции', 'Прочитанный статус', 'Решение', 'w=2 подтверждён'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-reconcile-operation: OK
';
Ожидаемый результат
mongodb129-write-reconcile-operation: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Представьте найденный документ с тем же ID и другим order. Введите отдельный исход «Конфликт данных» и остановите автоматический повтор. Для дальнейшей реализации свяжите ID операции с правилами идемпотентности и журналом сервиса.

Разделить ошибку элемента, подтверждения и команды

В журнале написано «MongoDB exception», но это мало помогает выбрать действие. Повторяющийся ID, невозможное подтверждение и неизвестная команда требуют разных исправлений. Сопоставим их в одной небольшой таблице без смешивания объектов разных API.

Входные данные и понятия

Сначала хранится existing/state=original. Вторая вставка того же _id с другим state вызывает duplicate11000. Затем новая запись confirmed-later сохраняется с w=2 и даёт concern100. Наконец отправляется заведомо несуществующая диагностическая команда с ошибкой59.

Путь операции и результата
  1. Duplicate _id → BulkWriteException → WriteError11000, inserted0
  2. Новая запись w2 → BulkWriteException → WriteConcernError100, inserted1
  3. Неизвестная команда → CommandException → reply code59
  4. Отдельное чтение: original не заменён, новая запись saved существует

Минимальная запись

$items = $writeResult->getWriteErrors();
$concern = $writeResult->getWriteConcernError();
// Отдельный CommandException даёт getResultDocument(), а не WriteResult.
// Пустой список WriteErrors не означает, что требуемое подтверждение получено.

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

СлойКодWriteErrorsConcern отсутствуетInserted
Элемент записи110001true0
Подтверждение1000false1
Команда59NULLNULLNULL

Почему получился такой результат

getWriteErrors возвращает ошибки отдельных поручений в BulkWrite. getWriteConcernError описывает отдельный слой подтверждения и может быть null. У CommandException другой результат: серверный документ с ok/code/codeName, а не счётчики WriteResult. NULL в соответствующих колонках таблицы означает «этот показатель к данному типу результата не применяется», а не успешный ноль.

Границы и типичные ошибки

Пустой список ошибок отдельных элементов не доказывает отсутствия concern error. И один общий getMessage не заменяет сведения о каждом слое. Пример не строит частичный импорт — такие сценарии разобраны в главе о bulk results; здесь важна классификация и привязка к данным. Текст duplicate содержит имя учебной коллекции, поэтому не используется как точное условие программы.

Полная самостоятельная программа и архив
Разделить ошибку элемента, подтверждения и командыСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'existing','state'=>'original']]);
    $duplicate=new MongoDB\Driver\BulkWrite;$duplicate->insert(['_id'=>'existing','state'=>'replacement']);
    [$a,$duplicateError]=mw129Attempt($manager,$namespace,$duplicate,1);
    if(!$duplicateError || count($a->getWriteErrors())!==1 || $a->getWriteErrors()[0]->getCode()!==11000){throw new RuntimeException('Expected duplicate');}
    $pending=new MongoDB\Driver\BulkWrite;$pending->insert(['_id'=>'confirmed-later','state'=>'saved']);
    [$b,$concernError]=mw129Attempt($manager,$namespace,$pending,2);$concern=mw129RequireConcern($b,$concernError,100);
    $commandError=mw129UnknownCommand($manager,$db);$reply=$commandError->getResultDocument();
    $saved=mw129Find($manager,$namespace,[],['_id'=>1,'state'=>1]);
    $checks=['item_error_no_concern'=>$a->getWriteConcernError()===null,'concern_no_item_errors'=>$b->getWriteErrors()===[],'command_reply_failed'=>(int)$reply->ok===0 && $reply->code===59,'two_documents_remain'=>count($saved)===2,'original_unchanged'=>$saved[1]['state']==='original','second_write_applied'=>$saved[0]['state']==='saved'];
    $details=['stored'=>$saved,'command_code_name'=>$reply->codeName,'duplicate_message'=>$a->getWriteErrors()[0]->getMessage(),'concern_message'=>$concern->getMessage()];
    return [['Элемент записи',11000,count($a->getWriteErrors()),$a->getWriteConcernError()===null,0],['Подтверждение',100,count($b->getWriteErrors()),$b->getWriteConcernError()===null,1],['Команда',59,null,null,null]];
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-failure-layers',['Слой', 'Код', 'WriteErrors', 'Concern отсутствует', 'Inserted'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-failure-layers: OK
';
Ожидаемый результат
mongodb129-write-failure-layers: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Добавьте понятное действие к каждой строке: проверить конфликт ID; исправить политику/топологию подтверждения и сверить запись; исправить имя команды. Не назначайте всем трём случаям один автоматический retry.

Вопрос из практики: What's the Difference Between 'err' and 'errmsg' in a PHP MongoDB Driver Result Array?. Исторический вопрос путает сообщение ошибки операции и команды и пытается выбрать одно поле вместо различения уровней. Обсуждение использовано как постановка задачи; приведённые здесь программы написаны заново по официальным современным контрактам.

Получить код ошибки команды и проверить, что данные не изменились

После обновления интеграции неправильно написано имя команды. Нужно увидеть структурированную причину отказа и убедиться, что диагностика не выдала себя за успешную запись. Для этой задачи CommandException хранит отдельный документ ответа.

Входные данные и понятия

Контрольная запись health-check/status=unchanged уже сохранена. Команда phpmanUnknownDiagnostic129 специально не существует. Сервер возвращает ok=0, code=59 и codeName=CommandNotFound; полный errmsg доступен в подробностях, но текст сообщения не разбирается программой.

Путь операции и результата
  1. Известная контрольная запись → сохранить w1
  2. Неизвестное имя команды → CommandException
  3. getResultDocument → object с ok/code/codeName/errmsg
  4. Повторное чтение контрольной записи → unchanged

Минимальная запись

try {
    $manager->executeReadCommand($db, new MongoDB\Driver\Command([
        'phpmanUnknownDiagnostic129'=>1,
    ]));
} catch (MongoDB\Driver\Exception\CommandException $error) {
    $reply = $error->getResultDocument(); // объект, доступ через ->
    $code = $reply->code ?? null;
}

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

ИсключениеokcodecodeNameКонтрольная запись
MongoDB\Driver\Exception\CommandException059CommandNotFoundunchanged

Почему получился такой результат

getResultDocument возвращает объект, поэтому поля читают через ->code, а не массивный индекс. ?? null позволяет заметить отсутствие поля в общем обработчике; наш фиксированный случай проверяет ожидаемую форму. На ext2.5.5 readonly resultDocument является тем же объектом, что getter. Неизвестную команду бессмысленно повторять без изменения запроса.

Границы и типичные ошибки

Это конкретный безопасный отказ неизвестной команды, а не утверждение, что любые CommandException никогда не оставляют эффекта. Для других команд надо знать их семантику. Документ ответа может содержать дополнительные поля, технические имена и значения; наружу выдавайте согласованный минимум. ConnectionException может возникнуть вообще без такого документа.

Полная самостоятельная программа и архив
Получить код ошибки команды и проверить, что данные не изменилисьСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'health-check','status'=>'unchanged']]);
    $error=mw129UnknownCommand($manager,$db);$reply=$error->getResultDocument();
    $saved=mw129Find($manager,$namespace,['_id'=>'health-check'],['_id'=>0,'status'=>1])[0];
    $checks=['reply_is_object'=>is_object($reply),'command_failed'=>(int)$reply->ok===0,'command_not_found'=>$reply->code===59 && $reply->codeName==='CommandNotFound','message_present'=>is_string($reply->errmsg) && $reply->errmsg!=='','readonly_same_object'=>$error->resultDocument===$reply,'fixture_unchanged'=>$saved['status']==='unchanged'];
    $details=['reply_selected_fields'=>['ok'=>$reply->ok,'code'=>$reply->code,'codeName'=>$reply->codeName,'errmsg'=>$reply->errmsg],'request'=>'phpmanUnknownDiagnostic129','retry_performed'=>false];
    return [[get_debug_type($error),(int)$reply->ok,$reply->code,$reply->codeName,$saved['status']]];
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-command-document',['Исключение', 'ok', 'code', 'codeName', 'Контрольная запись'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-command-document: OK
';
Ожидаемый результат
mongodb129-write-command-document: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Сделайте пользовательское сообщение «Операция временно недоступна, код обращения INC-18», а оператору оставьте code/codeName и имя действия. Не отдавайте клиенту полный серверный ответ и трассировку.

Проверить метки исключения и не выдумать разрешение на повтор

Обработчик видит знакомое слово «временная ошибка» и собирается повторять всё подряд. Вместо догадки нужно проверить метки конкретного исключения, а затем применить правила конкретной операции. Посмотрим, какие метки действительно есть у двух отказов нашего стенда.

Входные данные и понятия

Один duplicate11000 и одна неизвестная команда59. Для каждого вызываются hasErrorLabel с RetryableWriteError, TransientTransactionError, UnknownTransactionCommitResult и NoWritesPerformed. Это обычные строковые имена меток; их наличие определяет сервер или расширение, программа не дописывает их вручную.

Путь операции и результата
  1. Реальное исключение → hasErrorLabel(точное имя)
  2. Получить четыре bool для каждой ошибки
  3. На этом стенде все восемь значений false
  4. Фиксированные ошибки не повторяются; исходная запись остаётся kept

Минимальная запись

$retryable = $error->hasErrorLabel('RetryableWriteError');
$unknownCommit = $error->hasErrorLabel('UnknownTransactionCommitResult');
// Это признаки конкретного исключения, не команда повторить весь бизнес-процесс.
// false для NoWritesPerformed не доказывает, что данные изменились или не изменились.

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

СлучайRetryableWriteErrorTransientTransactionErrorUnknownTransactionCommitResultNoWritesPerformedДействие
duplicatefalsefalsefalsefalseПовтор не отправлен
commandfalsefalsefalsefalseПовтор не отправлен

Почему получился такой результат

hasErrorLabel возвращает true/false, а не запускает восстановление. Метки TransientTransactionError и UnknownTransactionCommitResult относятся к разным ситуациям транзакции; они не взаимозаменяемы. NoWritesPerformed — положительное свидетельство в определённых сценариях retryable writes, но отсутствие этой метки ничего не доказывает само по себе. Наши действия имеют по одной попытке и retryWrites=false.

Границы и типичные ошибки

Здесь реально проверено отсутствие меток у выбранных ошибок. Временный сетевой сбой, commit ambiguity и успешный автоматический retry не имитируются. Нельзя переносить отсутствие меток на все серверные ошибки. Встроенные retryable writes драйвера и ручное повторение бизнес-процесса — разные механизмы; правильная политика зависит от операции, версии, топологии и идемпотентности.

Полная самостоятельная программа и архив
Проверить метки исключения и не выдумать разрешение на повторСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'fixed','state'=>'kept']]);
    $bulk=new MongoDB\Driver\BulkWrite;$bulk->insert(['_id'=>'fixed','state'=>'changed']);
    [$result,$duplicate]=mw129Attempt($manager,$namespace,$bulk,1);
    if(!$duplicate || count($result->getWriteErrors())!==1 || $result->getWriteErrors()[0]->getCode()!==11000){throw new RuntimeException('Expected duplicate');}
    $command=mw129UnknownCommand($manager,$db);$rows=[];$allFalse=true;
    foreach(['duplicate'=>$duplicate,'command'=>$command] as $kind=>$error){$labels=mw129Labels($error);$allFalse=$allFalse && !in_array(true,$labels,true);$rows[]=[$kind,$labels['RetryableWriteError'],$labels['TransientTransactionError'],$labels['UnknownTransactionCommitResult'],$labels['NoWritesPerformed'],'Повтор не отправлен'];}
    $saved=mw129Find($manager,$namespace,['_id'=>'fixed'],['_id'=>0,'state'=>1])[0];
    $checks=['labels_observed_absent'=>$allFalse,'two_error_families'=>count($rows)===2,'original_unchanged'=>$saved['state']==='kept','no_retry'=>true];
    $details=['retryWrites'=>false,'attempts_per_action'=>1,'absence_of_label_proves_no_write'=>false];return $rows;
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-label-policy',['Случай', 'RetryableWriteError', 'TransientTransactionError', 'UnknownTransactionCommitResult', 'NoWritesPerformed', 'Действие'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-label-policy: OK
';
Ожидаемый результат
mongodb129-write-label-policy: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Нарисуйте отдельные ветви: исправляемый ввод, конфликт, неизвестный результат, ошибка конфигурации. Для каждой укажите доступные доказательства и дальнейшее действие. Не заменяйте такую схему бесконечным while с повтором.

Перенести старый обработчик WriteException на современный API

После перехода на ext-mongodb2 приложение перестало ловить ошибки записи старым catch WriteException. Исправим обработчик так, чтобы получить WriteResult на текущем расширении и явно показать, какой исторический API исчез.

Входные данные и понятия

Существующая запись получает повторную вставку того же ID. Это даёт настоящий BulkWriteException. class_exists проверяет старое имя без создания объекта и без вызова удалённого метода. В стенде2.5.5 результат false.

Путь операции и результата
  1. Duplicate в собственной коллекции → BulkWriteException
  2. Современный catch → getWriteResult и readonly writeResult
  3. Проверить ServerException как нынешнего родителя
  4. Старый WriteException отсутствует; сохранённый title не заменён

Минимальная запись

try {
    $result = $manager->executeBulkWrite($namespace, $bulk);
} catch (MongoDB\Driver\Exception\BulkWriteException $error) {
    $result = $error->getWriteResult();
    // Не catch WriteException: этот класс удалён в ext-mongodb2.0.
}

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

Проверка миграцииРезультат
WriteException существуетfalse
BulkWriteException пойманtrue
Родитель ServerExceptiontrue
writeResult === getWriteResult()true
Сохранённый titleСохранённый документ

Почему получился такой результат

WriteException был абстрактным базовым классом и никогда не выбрасывался напрямую. Он устарел в1.20.0 и удалён в2.0.0; BulkWriteException с2.0 наследует ServerException. Поэтому нужен современный catch. С2.3 getter дополняет readonly-свойство writeResult; проверка === показывает один объект, а не два независимых запроса к базе.

Границы и типичные ошибки

Старая справочная страница метода нужна для чтения старых проектов, но её сигнатура не обещает доступность класса в2.x. Этот опыт не выполняет исторический getWriteResult и не запускается на1.x ради формальной отметки. При поддержке старых окружений фиксируйте версии зависимостей и отдельную совместимость; не создавайте класс-заглушку с именем удалённого API.

Полная самостоятельная программа и архив
Перенести старый обработчик WriteException на современный APIСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'existing','title'=>'Сохранённый документ']]);
    $bulk=new MongoDB\Driver\BulkWrite;$bulk->insert(['_id'=>'existing','title'=>'Не должен заменить']);
    $caught=false;$legacy=class_exists('MongoDB\\Driver\\Exception\\WriteException');
    try{$manager->executeBulkWrite($namespace,$bulk,['writeConcern'=>new MongoDB\Driver\WriteConcern(1,250)]);}
    catch(MongoDB\Driver\Exception\BulkWriteException $error){$caught=true;$result=$error->getWriteResult();$propertySame=$error->writeResult===$result;$serverParent=$error instanceof MongoDB\Driver\Exception\ServerException;}
    if(!$caught){throw new RuntimeException('Expected modern exception');}
    $saved=mw129Find($manager,$namespace,['_id'=>'existing'],['_id'=>0,'title'=>1])[0];
    $checks=['modern_catch_works'=>$caught,'legacy_removed'=>$legacy===false,'server_exception_parent'=>$serverParent,'readonly_result_matches'=>$propertySame,'duplicate_not_applied'=>$result->getWriteErrors()[0]->getCode()===11000 && $saved['title']==='Сохранённый документ'];
    $details=['extension'=>phpversion('mongodb'),'modern_class'=>get_debug_type($error),'old_api_called'=>false,'stored'=>$saved];
    return [['WriteException существует',$legacy],['BulkWriteException пойман',$caught],['Родитель ServerException',$serverParent],['writeResult === getWriteResult()',$propertySame],['Сохранённый title',$saved['title']]];
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-modern-catch',['Проверка миграции', 'Результат'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-modern-catch: OK
';
Ожидаемый результат
mongodb129-write-modern-catch: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Найдите в своём проекте catch для WriteException и проверьте требуемую версию расширения. Замените обработчик на BulkWriteException, сохранив разделение ошибок элементов и подтверждения; неизвестные ошибки не превращайте в успех.

Собрать понятную карточку сбоя без полного дампа соединения

Пользователю нужен понятный исход действия, а оператору — достаточно сведений для разбора. Полный var_dump соединения или exception одновременно неудобен и может раскрыть внутренние данные. Соберём короткие карточки двух разных сбоев.

Входные данные и понятия

Одна запись incident-17 меняется before→after с w=2; другая диагностическая команда неизвестна серверу. Для отчёта отбираются incident, kind, code, наблюдаемое состояние и следующее действие. В операторских подробностях отдельно находятся адрес учебного сервера, сообщение concern и codeName команды.

Путь операции и результата
  1. Два реальных отказа → свои типизированные результаты
  2. Выбрать code/слой + наблюдаемый status; проверить метки
  3. Сформировать согласованный массив отчёта без URI и trace
  4. HTML-таблица: INC-17 требует сверки, INC-18 исправления команды

Минимальная запись

$report = [
    'incident'=>'INC-17', 'kind'=>'write_concern',
    'code'=>$concern->getCode(), 'state'=>$observedStatus,
];
// Для оператора выбираем нужные поля; не публикуем URI, trace или полный getInfo сервера.

Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.

Результат

ИнцидентСлойКодНаблюдаемое состояниеСледующее действие
INC-17write_concern100afterСверить подтверждение; не повторять изменение
INC-18command59Не менялась этой командойИсправить имя команды

Почему получился такой результат

Код100 вместе с прочитанным after описывает применённое изменение с недостаточным подтверждением. Код59 относится к неизвестной команде; её повтор не поможет. getServer нужен для технической привязки к записи, но его getInfo не является подробностями ошибки. В отчёт не копируются полный URI, stack trace или технический ответ целиком. HTML helper экранирует отображаемые значения.

Границы и типичные ошибки

Это учебная карточка инцидента, а не уже внедрённый серверный журнал или HTTP API. В реальном приложении разделите доступ пользователя и оператора, сроки хранения и чувствительные поля. Метки здесь проверены, но retry не выполняется. Выбор нужных полей не равен универсальной автоматической очистке произвольного диагностического объекта.

Полная самостоятельная программа и архив
Собрать понятную карточку сбоя без полного дампа соединенияСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/mw129-helper.php';
[$uri,$db]=mw129Config();
$manager=new MongoDB\Driver\Manager($uri,['connectTimeoutMS'=>1000,'serverSelectionTimeoutMS'=>2000,'socketTimeoutMS'=>2500,'retryReads'=>false]);
$hello=mw129RequireLab($manager,$db);
$runtime=['php'=>PHP_VERSION,'extension'=>phpversion('mongodb'),'server'=>mw129Command($manager,$db,['buildInfo'=>1])[0]['version'],'execution'=>'database','replica_set'=>$hello['setName'],'members'=>count($hello['hosts'])];
$checks=[];$details=[];
$rows=mw129WithCollection($manager,$db,function(string $namespace,string $collection) use($manager,$db,&$checks,&$details): array {
mw129Insert($manager,$namespace,[['_id'=>'incident-17','status'=>'before']]);
    $bulk=new MongoDB\Driver\BulkWrite;$bulk->update(['_id'=>'incident-17'],['$set'=>['status'=>'after']],['multi'=>false,'upsert'=>false]);
    [$result,$error]=mw129Attempt($manager,$namespace,$bulk,2);$concern=mw129RequireConcern($result,$error,100);
    $server=$result->getServer();$info=$concern->getInfo();$spec=$info->writeConcern??null;
    $command=mw129UnknownCommand($manager,$db);$reply=$command->getResultDocument();
    $saved=mw129Find($manager,$namespace,['_id'=>'incident-17'],['_id'=>0,'status'=>1])[0];
    $reports=[['incident'=>'INC-17','kind'=>'write_concern','code'=>$concern->getCode(),'state'=>$saved['status'],'action'=>'Сверить подтверждение; не повторять изменение'],['incident'=>'INC-18','kind'=>'command','code'=>$reply->code,'state'=>'Не менялась этой командой','action'=>'Исправить имя команды']];
    $checks=['write_observed_after'=>$saved['status']==='after','only_needed_codes'=>array_column($reports,'code')===[100,59],'no_raw_uri'=>!str_contains(mw129Json($reports),'mongodb://'),'labels_checked'=>$error->hasErrorLabel('RetryableWriteError')===false && $command->hasErrorLabel('RetryableWriteError')===false,'no_retry'=>true];
    $details=['operator_detail'=>['server_host'=>$server->getHost(),'server_port'=>$server->getPort(),'write_code'=>$concern->getCode(),'write_message'=>$concern->getMessage(),'requested_w'=>$spec?->w,'command_code'=>$reply->code,'command_name'=>$reply->codeName],'reports'=>$reports,'trace_or_uri_exposed'=>false];
    return array_map(fn($r)=>[$r['incident'],$r['kind'],$r['code'],$r['state'],$r['action']],$reports);
});
$checks['owned_collection_removed']=true;
mw129Report('mongodb129-write-incident-view',['Инцидент', 'Слой', 'Код', 'Наблюдаемое состояние', 'Следующее действие'],$rows,$checks,$runtime,$details);
echo 'mongodb129-write-incident-view: OK
';
Ожидаемый результат
mongodb129-write-incident-view: OK

Настоящие записи, исключения и ограниченные чтения в отдельной одноузловой MongoDB. Нет фиктивных серверных ошибок или повторных бизнес-записей; helper и инструкция находятся в ZIP.

Открыть фактическую таблицу и подробности · Данные результата JSON

Упражнение с направлением решения. Добавьте в операторский отчёт время и собственный correlation ID запроса, не включая пароль или весь пользовательский payload. Для публичного ответа оставьте только понятный статус и ID обращения; технический codeName сохраните в защищённом журнале.

Классы и методы диагностики записи

Двенадцать карточек связаны с рассмотренными задачами. Исторический метод WriteException явно отмечен удалённым и сопровождается современной миграцией. Остальные новые программы получают объекты от настоящего драйвера; исключения и WriteConcernError не конструируются вручную для имитации ответа.

Класс или методНазначение
MongoDB\Driver\WriteConcernErrorРазобрать отдельную ошибку требуемого уровня подтверждения записи.
MongoDB\Driver\WriteConcernError::getCodeПолучить числовую причину недостаточного подтверждения для отчёта и выбора дальнейшей проверки.
MongoDB\Driver\WriteConcernError::getInfoПрочитать дополнительный документ о причине ошибки подтверждения.
MongoDB\Driver\WriteConcernError::getMessageПолучить текстовое объяснение ошибки подтверждения для человека.
MongoDB\Driver\WriteResult::getServerУзнать, какой сервер выполнил BulkWrite, связанный с этой квитанцией.
MongoDB\Driver\WriteResult::getWriteConcernErrorПроверить отдельный слой подтверждения результата записи.
MongoDB\Driver\Exception\BulkWriteException::getWriteResultДостать сведения о выполненной части операции из пойманной ошибки BulkWrite.
MongoDB\Driver\Exception\WriteException::getWriteResultПонять старый обработчик ошибок записи и перенести его на BulkWriteException.
MongoDB\Driver\Exception\CommandException::getResultDocumentПрочитать серверный документ ответа на неуспешную команду.
MongoDB\Driver\Exception\RuntimeException::hasErrorLabelПроверить, присвоена ли конкретная метка этому исключению.
MongoDB\Driver\Exception\BulkWriteExceptionПоймать отказ BulkWrite, сохранив сведения о фактическом результате.
MongoDB\Driver\Exception\CommandExceptionПоймать ошибку команды и сохранить её структурированный ответ.

От квитанции к обоснованному дальнейшему действию

Разбирая сбой, сначала определите слой: отдельная операция, подтверждение, команда или соединение. Затем прочитайте доступные структурированные сведения, сопоставьте их с известным ID операции и нужной политикой сохранности. Наблюдаемая запись, ответ с подтверждением одного узла и гарантия большинства — разные факты. Повторять действие следует только по отдельному понятному правилу, а не из-за самого наличия catch.

Далее: write concern и read concern, транзакции и границы подтверждения, условные атомарные изменения, подробные результаты пакетного импорта, отказы команд. Полное дерево API доступно через справочник.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

MongoDB\Driver\WriteConcernErrorРазобрать отдельную ошибку требуемого уровня подтверждения записи.

Класс и три getter с ext-mongodb1.0.0. Public readonly свойства message/code/info добавлены в2.3.0. · mongodb

final class MongoDB\Driver\WriteConcernError { public readonly string $message; public readonly int $code; public readonly ?object $info; final public function getCode(): int; final public function getInfo(): ?object; final public function getMessage(): string; }

Параметры и вызов

Объект получают из WriteResult::getWriteConcernError(); пользовательские параметры constructor в этом API не описаны. Три метода без аргументов дают code/message/info. С ext2.3 эти же сведения доступны через public readonly свойства.

Возвращаемое значение

Описание write concern error, не само исключение и не новый WriteResult. Его наличие означает, что нужное подтверждение не получено; оно само по себе не говорит, какие изменения уже применились.

Ошибки и границы

Проверяйте null результата getWriteConcernError до вызова методов. У объекта ошибки нет сетевого повторного запроса; у getter официально указаны ошибки разбора аргументов.

Это другой тип, чем WriteError отдельного поручения и CommandException ошибки команды. Подробности getInfo могут отсутствовать. В опыте w2 на одном узле ошибка100 наблюдается вместе с уже сохранённым документом.

Версии

Класс и три getter с ext-mongodb1.0.0. Public readonly свойства message/code/info добавлены в2.3.0.

Остановить слепой повтор изменения

$concern = $result->getWriteConcernError();
if ($concern !== null) { $code = $concern->getCode(); }

Результат позволяет отличить отказ подтверждения от полного отсутствия записи; состояние счётчика читается отдельно.

Полная программа, исходные данные и результат

Отличить число подтверждений от именованного режима

$code = $concern->getCode();
$message = $concern->getMessage();
$info = $concern->getInfo();

Фактические коды100/79 сопоставлены с разными запрошенными w; все сохранённые документы проверены чтением.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\WriteConcernError::getCodeПолучить числовую причину недостаточного подтверждения для отчёта и выбора дальнейшей проверки.

С ext-mongodb1.0.0; readonly свойство code доступно с2.3.0. · mongodb

final public function MongoDB\Driver\WriteConcernError::getCode(): int

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

Целое число кода ошибки write concern. В нашем опыте100 — UnsatisfiableWriteConcern,79 — UnknownReplWriteConcern. Это не количество неуспешных документов и не номер операции в bulk.

Ошибки и границы

Документация указывает MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов. Сам getter не повторяет запись. Ошибки сети или выполнения возникли на этапе обращения к базе.

Не приравнивайте любой код к timeout64. Ветвление должно учитывать слой ошибки и версию сервера; текст сообщения нужен человеку, но не заменяет код.

Версии

С ext-mongodb1.0.0; readonly свойство code доступно с2.3.0.

Сгруппировать две причины в операторской таблице

$reason = $concern->getCode();

Код сопоставляется со структурой запрошенного w; программа не ищет слова в английском message.

Полная программа, исходные данные и результат

Сохранить компактную карточку инцидента

$report = ['kind'=>'write_concern', 'code'=>$concern->getCode()];

Число100 и слой передаются вместе с наблюдаемым статусом after; полная трассировка в карточку не попадает.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\WriteConcernError::getInfoПрочитать дополнительный документ о причине ошибки подтверждения.

С ext-mongodb1.0.0; readonly свойство info с2.3.0. · mongodb

final public function MongoDB\Driver\WriteConcernError::getInfo(): ?object

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

Объект metadata либо null, если дополнительных сведений нет. Конкретные поля зависят от ошибки и версии сервера. В опыте имеется вложенный объект writeConcern с w, wtimeout и provenance.

Ошибки и границы

Документация указывает MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов. Сам getter не повторяет запись. Ошибки сети или выполнения возникли на этапе обращения к базе.

Не считайте наличие wtimeout в metadata доказательством таймаута. Не переносите пример из документации с info.wtimeout=true на все ошибки. getServer()->getInfo() описывает сервер, а не эту ошибку.

Версии

С ext-mongodb1.0.0; readonly свойство info с2.3.0.

Выяснить, какой режим запросил клиент

$info = $concern->getInfo();
$requested = $info->writeConcern ?? null;
if (is_object($requested)) { $w = $requested->w ?? null; }

Пустой или иной metadata не подменяется вымышленными значениями. В проверяемых случаях w равен2 или missingTag.

Полная программа, исходные данные и результат

Отобрать минимум для технического разбора

$info = $concern->getInfo();
$requestedW = $info->writeConcern->w ?? null;

Операторская карточка фиксирует запрошенное подтверждение отдельно от наблюдаемого состояния документа.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\WriteConcernError::getMessageПолучить текстовое объяснение ошибки подтверждения для человека.

С ext-mongodb1.0.0; readonly свойство message с2.3.0. · mongodb

final public function MongoDB\Driver\WriteConcernError::getMessage(): string

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

Строка сообщения write concern error. Для numeric w2 на стенде она равна Not enough data-bearing nodes; для отсутствующего режима содержит имя missingTag.

Ошибки и границы

Документация указывает MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов. Сам getter не повторяет запись. Ошибки сети или выполнения возникли на этапе обращения к базе.

Формулировка сообщения не является стабильной схемой данных. Не разбирайте её regex вместо code/info. При показе в HTML строку экранируют; в реальной диагностике учитывают внутренние имена и чувствительные сведения.

Версии

С ext-mongodb1.0.0; readonly свойство message с2.3.0.

Подпись причины рядом с её структурой

$message = $concern->getMessage();
$code = $concern->getCode();

Подробности фактических двух ответов сохраняются для человека; решение проверяется по коду и структуре.

Полная программа, исходные данные и результат

Техническое сообщение по ID обращения

$operator = ['message'=>$concern->getMessage()];

Публичная таблица содержит понятное следующее действие, а подробности — выбранное сообщение без URI и trace.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\WriteResult::getServerУзнать, какой сервер выполнил BulkWrite, связанный с этой квитанцией.

С ext-mongodb1.0.0. · mongodb

final public function MongoDB\Driver\WriteResult::getServer(): MongoDB\Driver\Server

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

Объект Server, ассоциированный с WriteResult. Его методы позволяют прочитать адрес и тип сервера. Getter не заново выбирает подходящий сервер для следующего запроса.

Ошибки и границы

Документация указывает MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов. Сам getter не повторяет запись. Ошибки сети или выполнения возникли на этапе обращения к базе.

Server::getInfo — сведения сервера, а не подробности WriteError/WriteConcernError. Адрес из ответа может отличаться написанием от seed URI: наша реплика объявляет localhost, хотя подключение начато с127.0.0.1. Не используйте это как доказательство выполнения всех будущих операций на том же узле.

Версии

С ext-mongodb1.0.0.

Привязать успешную смену статуса к серверу

$server = $result->getServer();
$address = $server->getHost().':'.$server->getPort();

Квитанция и реальное чтение ready показаны вместе; адрес localhost:27076 находится в подробностях результата.

Полная программа, исходные данные и результат

Добавить адрес в технический разбор сбоя

$server = $error->getWriteResult()->getServer();

Адрес относится к ошибочной квитанции изменения; причина подтверждения читается отдельно из concern.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\WriteResult::getWriteConcernErrorПроверить отдельный слой подтверждения результата записи.

С ext-mongodb1.0.0. · mongodb

final public function MongoDB\Driver\WriteResult::getWriteConcernError(): ?MongoDB\Driver\WriteConcernError

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

WriteConcernError, если во время записи произошла ошибка write concern; иначе null. Это не массив ошибок отдельных поручений — их возвращает getWriteErrors.

Ошибки и границы

Документация указывает MongoDB\Driver\Exception\InvalidArgumentException при ошибке разбора аргументов. Сам getter не повторяет запись. Ошибки сети или выполнения возникли на этапе обращения к базе.

Проверяйте null прежде, чем обращаться к getCode. Отсутствие concern error не означает отсутствия WriteError, а isAcknowledged не заменяет эту проверку. В случаях исключения WriteResult берут из BulkWriteException.

Версии

С ext-mongodb1.0.0.

Отметить отсутствие проблемы подтверждения

$hasConcernError = $result->getWriteConcernError() !== null;

Нормальная запись w1 даёт null, в том же опыте отдельно проверены счётчики и сохранённый статус.

Полная программа, исходные данные и результат

Разделить конфликт ID и недостаточное подтверждение

$items = $result->getWriteErrors();
$concern = $result->getWriteConcernError();

Нельзя заменить эти две проверки одним «есть exception»: ошибочные операции имеют разные последствия.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\Exception\BulkWriteException::getWriteResultДостать сведения о выполненной части операции из пойманной ошибки BulkWrite.

С ext-mongodb1.0.0. Readonly writeResult в BulkWriteException доступен с2.3.0. · mongodb

final public function MongoDB\Driver\Exception\BulkWriteException::getWriteResult(): MongoDB\Driver\WriteResult

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

WriteResult ошибочной операции. Его getWriteErrors и getWriteConcernError раскрывают соответствующие уровни; счётчики могут показывать выполненные изменения. Возвращаемый объект не является повтором исходной записи.

Ошибки и границы

Официальная страница метода не перечисляет отдельные исключения. Метод читает сведения из пойманной ошибки, а не выполняет запрос повторно.

Наличие исключения не обнуляет счётчики автоматически. Не присваивайте null всем результатам catch и не запускайте ту же бизнес-операцию до разбора исхода. Для современных BulkWriteCommand есть другой класс исключения и другой result API.

Версии

С ext-mongodb1.0.0. Readonly writeResult в BulkWriteException доступен с2.3.0.

Увидеть изменение за ошибкой подтверждения

$result = $error->getWriteResult();
$modified = $result->getModifiedCount();
$concern = $result->getWriteConcernError();

Счётчик modified=1 и отдельное чтение reserved=6 не дают ошибочно выполнить второй $inc.

Полная программа, исходные данные и результат

Разнести ошибки по слоям

$items = $result->getWriteErrors();
$concern = $result->getWriteConcernError();

Duplicate даёт WriteError11000 и null concern; другая запись даёт concern100 и пустой список ошибок элементов.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\Exception\WriteException::getWriteResultПонять старый обработчик ошибок записи и перенести его на BulkWriteException.

Метод существовал с ext-mongodb1.0.0. Класс WriteException устарел в1.20.0 и удалён в2.0.0; он никогда не выбрасывался напрямую. До2.0 BulkWriteException был его наследником, теперь наследует ServerException. · mongodb

final public function MongoDB\Driver\Exception\WriteException::getWriteResult(): MongoDB\Driver\WriteResult

Параметры и вызов

Историческая сигнатура не имеет аргументов. Метод находился у абстрактного WriteException. На ext-mongodb2.x класс удалён; следующий код применения использует современный BulkWriteException, а не вызывает этот исторический метод.

Возвращаемое значение

В ext1.x возвращал WriteResult неуспешной записи с getWriteErrors/getWriteConcernError. В2.x получение этого результата выполняют через BulkWriteException::getWriteResult.

Ошибки и границы

Страница метода не перечисляет отдельные исключения. Удалённый класс нельзя создавать или считать существующим на2.x; class_exists позволяет диагностировать наличие имени без такого вызова.

Эта карточка — исторический контракт и путь миграции. Исполнение удалённого API на стенде2.5.5 не заявляется. Не заводите одноимённый фиктивный класс, чтобы скрыть несовместимость.

Версии

Метод существовал с ext-mongodb1.0.0. Класс WriteException устарел в1.20.0 и удалён в2.0.0; он никогда не выбрасывался напрямую. До2.0 BulkWriteException был его наследником, теперь наследует ServerException.

Исправить catch после обновления расширения

catch (MongoDB\Driver\Exception\BulkWriteException $error) {
    $result = $error->getWriteResult();
}

Рабочая современная программа подтверждает catch и отсутствие старого имени. Это часть try/catch, не самостоятельная программа.

Полная программа, исходные данные и результат

Сохранить раздельную обработку ошибок при миграции

$result = $duplicateError->getWriteResult();
$items = $result->getWriteErrors();
$concern = $result->getWriteConcernError();

Меняется класс обработчика, а необходимость понимать ошибки элементов и подтверждения сохраняется; обе современные ветви показаны реальными запросами.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\Exception\CommandException::getResultDocumentПрочитать серверный документ ответа на неуспешную команду.

С ext-mongodb1.5.0. Readonly resultDocument у CommandException добавлен в2.3.0. · mongodb

final public function MongoDB\Driver\Exception\CommandException::getResultDocument(): object

Параметры и вызов

Аргументов нет. Метод читает уже полученный объект результата/ошибки; нового запроса в MongoDB не выполняет.

Возвращаемое значение

Объект документа результата ошибочной команды. Наш пример содержит ok, code, codeName и errmsg; дополнительные поля зависят от команды и сервера. Это не WriteResult и не PHP-массив по контракту.

Ошибки и границы

Официальная страница метода не перечисляет отдельные исключения. Метод читает сведения из пойманной ошибки, а не выполняет запрос повторно.

Не предполагается, что любая ошибка сети имеет такой документ. Поля проверяют перед доступом в универсальном обработчике. Полный ответ не следует без отбора выдавать пользователю.

Версии

С ext-mongodb1.5.0. Readonly resultDocument у CommandException добавлен в2.3.0.

Отличить неподдерживаемую команду от сетевой ошибки

$reply = $error->getResultDocument();
$code = $reply->code ?? null;
$name = $reply->codeName ?? null;

Реально получены code59/CommandNotFound и неизменная контрольная запись.

Полная программа, исходные данные и результат

Записать нужную причину в карточку инцидента

$reply = $command->getResultDocument();
$report = ['kind'=>'command', 'code'=>$reply->code ?? null];

Краткий формат не смешивает command-code с числами изменённых записей и не публикует весь ответ.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\Exception\RuntimeException::hasErrorLabelПроверить, присвоена ли конкретная метка этому исключению.

С ext-mongodb1.6.0 вместе с поддержкой errorLabels. · mongodb

final public function MongoDB\Driver\Exception\RuntimeException::hasErrorLabel(string $errorLabel): bool

Параметры и вызов

$errorLabel — обязательное строковое имя, например TransientTransactionError или UnknownTransactionCommitResult. Метки выставляет сервер или расширение. Не читайте protected errorLabels вручную: для проверки предназначен этот метод.

Возвращаемое значение

true, если указанная метка связана с исключением; false, если нет. Метод ничего не повторяет и не определяет сам по себе, какой бизнес-шаг можно безопасно воспроизвести.

Ошибки и границы

Официальная страница метода не перечисляет отдельные исключения. Метод читает сведения из пойманной ошибки, а не выполняет запрос повторно.

Контекст транзакции, обычной записи и commit различается. Отсутствие NoWritesPerformed не доказывает ни применение, ни неприменение записи. Наши реальные ошибки не имеют перечисленных меток; сценарий положительной transient-метки здесь не объявляется проверенным.

Версии

С ext-mongodb1.6.0 вместе с поддержкой errorLabels.

Проверить реальные метки двух отказов

$retryable = $error->hasErrorLabel('RetryableWriteError');
$noWrites = $error->hasErrorLabel('NoWritesPerformed');

Два типа ошибок дают false; программа сохраняет наблюдение и не делает автоматических попыток.

Полная программа, исходные данные и результат

Не выдавать оператору выдуманный совет retry

$retryable = $error->hasErrorLabel('RetryableWriteError');

Для concern100 и command59 метка отсутствует; в карточках выбраны исправление конфигурации/запроса и сверка данных.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\Exception\BulkWriteExceptionПоймать отказ BulkWrite, сохранив сведения о фактическом результате.

С ext-mongodb1.0.0. С2.0.0 родитель ServerException вместо удалённого WriteException. С2.3.0 writeResult — public readonly. · mongodb

class MongoDB\Driver\Exception\BulkWriteException extends MongoDB\Driver\Exception\ServerException implements MongoDB\Driver\Exception\Exception { public readonly MongoDB\Driver\WriteResult $writeResult; final public function getWriteResult(): MongoDB\Driver\WriteResult; }

Параметры и вызов

Исключение создаёт драйвер при неуспешной bulk-операции. Оно содержит writeResult/getWriteResult. Унаследованы hasErrorLabel, getMessage/getCode, getPrevious и стандартная диагностика Exception; отдельный пользовательский constructor для получения серверной ошибки не нужен.

Возвращаемое значение

Объект исключения с WriteResult: могут присутствовать ошибки отдельных операций или подтверждения. Это не исключение нового BulkWriteCommand; для него существует BulkWriteCommandException.

Ошибки и границы

Нормальное использование — try/catch вокруг executeBulkWrite. Не всякий runtime-сбой становится BulkWriteException: ошибки соединения/авторизации имеют свои классы. Не подавляйте неизвестную причину общим успешным ответом.

Не выводите «ничего не записано» только из класса исключения. Разберите оба слоя WriteResult и при необходимости сверяйте известную операцию. Унаследованные trace/file/line — техническая диагностика, обычно не часть публичного ответа.

Версии

С ext-mongodb1.0.0. С2.0.0 родитель ServerException вместо удалённого WriteException. С2.3.0 writeResult — public readonly.

Сохранить квитанцию несмотря на исключение

catch (MongoDB\Driver\Exception\BulkWriteException $error) {
    $result = $error->getWriteResult();
}

Код100 не мешает увидеть modified1 и реально прочитанное reserved6; catch показан в полном контексте программы.

Полная программа, исходные данные и результат

Проверить переход на современное дерево исключений

$isServerError = $error instanceof MongoDB\Driver\Exception\ServerException;
$same = $error->writeResult === $error->getWriteResult();

Реальная ошибка duplicate подтверждает нынешнего родителя и одинаковый объект из свойства/getter.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

MongoDB\Driver\Exception\CommandExceptionПоймать ошибку команды и сохранить её структурированный ответ.

С ext-mongodb1.5.0. Readonly публичное свойство resultDocument добавлено в2.3.0. · mongodb

class MongoDB\Driver\Exception\CommandException extends MongoDB\Driver\Exception\ServerException implements MongoDB\Driver\Exception\Exception { public readonly object $resultDocument; final public function getResultDocument(): object; }

Параметры и вызов

Класс выбрасывается при отказе команды. Метод без аргументов getResultDocument и свойство resultDocument содержат её результат. Унаследованы hasErrorLabel, стандартные getMessage/getCode/getPrevious и методы диагностики Exception.

Возвращаемое значение

Исключение, у которого доступен документ серверного ответа. Оно не обещает WriteResult и не превращает любой сетевой сбой в документ с codeName.

Ошибки и границы

Ловите конкретный CommandException там, где умеете разобрать эту команду. Ошибки аргументов и соединения рассматривают отдельно; не подставляйте фиктивный ответ при отсутствии реального серверного документа.

Содержимое ответа зависит от команды; наличие ошибки не определяет универсально побочные эффекты всех команд. В нашем опыте неизвестная команда не изменяет контрольный документ. Не публикуйте trace и полный resultDocument без отбора.

Версии

С ext-mongodb1.5.0. Readonly публичное свойство resultDocument добавлено в2.3.0.

Показать реальную причину отказа

catch (MongoDB\Driver\Exception\CommandException $error) {
    $reply = $error->getResultDocument();
}

Для неизвестного имени получен code59 и codeName CommandNotFound; полный try/catch находится в программе.

Полная программа, исходные данные и результат

Сформировать отдельный тип карточки команды

$reply = $command->getResultDocument();
$name = $reply->codeName ?? 'Unknown';

Причина команды отделена от concern100, поэтому следующая рекомендация — исправление команды, а не повтор записи.

Полная программа, исходные данные и результат

Связи: уровни подтверждения и чтения, WriteError и атомарное изменение, отказы команд.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.