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

Дерево руководства
Глава 171 · После BulkWrite и основ read/write concern; диагностика разобрана по шагам
MongoDB: подтверждение записи, реальные ошибки и диагностика результата
Почему exception не равен откату: сервер записи, WriteConcernError, сверка по ID, ошибки команды, метки и перенос старого WriteException.
Что произошло с данными, если пришло исключение
Вы нажали «Подготовить заказ», сервер отправил изменение в MongoDB, а приложение получило исключение. Можно ли написать пользователю «ничего не изменилось» и повторить действие? Не всегда: сама запись и подтверждение нужного уровня — разные шаги. Здесь получим понятные квитанции и сверим их с настоящими документами. После примеров будет видно, почему конфликт ID, недостаточное подтверждение и ошибка команды требуют разных решений.
- Приложение: order-17, status draft → ready
- Primary применяет изменение документа
- Сервер пытается выполнить запрошенный write concern
- PHP получает WriteResult или исключение с результатом → отдельно читает status
Primary — узел replica set, который принимает записи. Write concern — требование к подтверждению записи, например от одного узла или большинства. WriteResult похож на квитанцию: содержит счётчики и сведения об ошибках. Аналогия ограничена: квитанция не является новым чтением документа и не описывает все возможные будущие сбои.
$concern = $result->getWriteConcernError();
if ($concern === null) {
echo 'Ошибка подтверждения не указана';
}В этом небольшом фрагменте $result — уже полученный WriteResult. $concern сохраняет результат метода; -> вызывает метод объекта; пустые круглые скобки означают отсутствие аргументов. null означает отсутствие объекта ошибки. === сравнивает значение и тип. При нормальном первом примере вывод будет «Ошибка подтверждения не указана». Это не полная проверка успеха: ниже дополнительно разобраны ошибки элементов, счётчики и фактическое состояние.
Подготовка: коллекция и первый запрос, BulkWrite и executeBulkWrite, объекты и методы, подтверждение и чтение. Полные ZIP включают подключение и исходные данные; короткие фрагменты страницы выделяют текущий шаг.
Четыре вопроса к результату вместо общего «ошибка»
| Что проверяем | Где это находится | Наблюдение на стенде | Чего нельзя заключать |
|---|---|---|---|
| Ошибка одного поручения | WriteResult::getWriteErrors | Duplicate11000; existing не заменён | Что любая ошибка откатывает весь bulk |
| Уровень подтверждения | WriteResult::getWriteConcernError | w2 → код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.phpZIP содержит 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 ожидания репликации не применяется.
- order-17: draft → BulkWrite update $set ready
- executeBulkWrite(w=1) → WriteResult
- getServer → фактически выполнивший сервер; getWriteConcernError → null
- Отдельный find с проекцией status → ready в таблице
Минимальная запись
$result = $manager->executeBulkWrite($namespace, $bulk, [
'writeConcern' => new MongoDB\Driver\WriteConcern(1, 250),
]);
$server = $result->getServer();
$concern = $result->getWriteConcernError(); // null в этом успешном случаеФрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.
Результат
| Заказ | Прочитанный статус | Matched | Modified | Нет concern error | Сервер |
|---|---|---|---|---|---|
| order-17 | ready | 1 | 1 | true | RS 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.
- reserved=5 → один $inc +1
- Primary применяет изменение → reserved=6
- Запрошено w=2 при одном узле → BulkWriteException
- 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 |
| Код concern | 100 |
| WriteResult isAcknowledged | true |
| Повторный $inc | false |
Почему получился такой результат
Исключение означает нарушение условия выполнения, но не автоматическое отсутствие любого результата. В его 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.
- Запрос w=2 или missingTag → реальная вставка
- BulkWriteException → WriteResult → WriteConcernError
- getCode/getMessage/getInfo → число, текст, объект metadata
- Таблица показывает коды100/79, исходный w, 250ms и clientSupplied
Минимальная запись
$info = $concern->getInfo(); // ?object, иногда null
$requested = $info->writeConcern ?? null;
$code = $concern->getCode();
$message = $concern->getMessage();
// Сначала проверить форму metadata; текст сообщения не разбирать регулярным выражением.Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.
Результат
| Код | Запрошенный w | wtimeout, мс | Источник | Inserted |
|---|---|---|---|---|
| 100 | 2 | 250 | clientSupplied | 1 |
| 79 | missingTag | 250 | clientSupplied | 1 |
Почему получился такой результат
Код 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, а затем сверяет оба ожидаемых значения. Коллекция вымышленная и используется только одним процессом опыта.
- Заранее выбран shipment-2024-17 + ожидаемые order/status
- Одна вставка → concern100, inserted1
- Find по точному ID → сравнить order и packed
- Запись наблюдается → повтор не отправлен; требование w2 всё ещё не подтверждено
Минимальная запись
$found = mw129Find($manager, $namespace, ['_id'=>$operationId], [
'_id'=>1, 'order'=>1, 'status'=>1,
]);
// Сверить ID и ожидаемые значения. Наличие на primary не доказывает подтверждение w=2.Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.
Результат
| ID операции | Прочитанный статус | Решение | w=2 подтверждён |
|---|---|---|---|
| shipment-2024-17 | packed | Запись наблюдается; повтор не отправлен | false |
Почему получился такой результат
Сверка отличается от поиска «последнего документа»: ID связывает запрос и наблюдаемый результат. Одного совпавшего ID недостаточно, поэтому проверяется содержимое. Если ID найден с другим содержимым, это конфликт, а не успех. В итоговой таблице отдельно стоит false для подтверждения w=2; факт чтения не превращён в более сильную гарантию.
Границы и типичные ошибки
Это демонстрация наблюдения и осторожного решения, а не универсальная exactly-once схема. В распределённой системе нужны правила уникальности ID, повторов, хранения статуса и выбранные read/write concern. Параллельный процесс может изменить запись между шагами; наш отдельный стенд такого соревнования не моделирует. Отсутствие записи при одном чтении также не всегда доказывает, что запрос нигде не применился.
Полная самостоятельная программа и архив
<?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.
- Duplicate _id → BulkWriteException → WriteError11000, inserted0
- Новая запись w2 → BulkWriteException → WriteConcernError100, inserted1
- Неизвестная команда → CommandException → reply code59
- Отдельное чтение: original не заменён, новая запись saved существует
Минимальная запись
$items = $writeResult->getWriteErrors();
$concern = $writeResult->getWriteConcernError();
// Отдельный CommandException даёт getResultDocument(), а не WriteResult.
// Пустой список WriteErrors не означает, что требуемое подтверждение получено.Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.
Результат
| Слой | Код | WriteErrors | Concern отсутствует | Inserted |
|---|---|---|---|---|
| Элемент записи | 11000 | 1 | true | 0 |
| Подтверждение | 100 | 0 | false | 1 |
| Команда | 59 | NULL | NULL | NULL |
Почему получился такой результат
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 доступен в подробностях, но текст сообщения не разбирается программой.
- Известная контрольная запись → сохранить w1
- Неизвестное имя команды → CommandException
- getResultDocument → object с ok/code/codeName/errmsg
- Повторное чтение контрольной записи → 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, проверки и очистку своей коллекции.
Результат
| Исключение | ok | code | codeName | Контрольная запись |
|---|---|---|---|---|
| MongoDB\Driver\Exception\CommandException | 0 | 59 | CommandNotFound | unchanged |
Почему получился такой результат
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. Это обычные строковые имена меток; их наличие определяет сервер или расширение, программа не дописывает их вручную.
- Реальное исключение → hasErrorLabel(точное имя)
- Получить четыре bool для каждой ошибки
- На этом стенде все восемь значений false
- Фиксированные ошибки не повторяются; исходная запись остаётся kept
Минимальная запись
$retryable = $error->hasErrorLabel('RetryableWriteError');
$unknownCommit = $error->hasErrorLabel('UnknownTransactionCommitResult');
// Это признаки конкретного исключения, не команда повторить весь бизнес-процесс.
// false для NoWritesPerformed не доказывает, что данные изменились или не изменились.Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.
Результат
| Случай | RetryableWriteError | TransientTransactionError | UnknownTransactionCommitResult | NoWritesPerformed | Действие |
|---|---|---|---|---|---|
| duplicate | false | false | false | false | Повтор не отправлен |
| command | false | false | false | false | Повтор не отправлен |
Почему получился такой результат
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.
- Duplicate в собственной коллекции → BulkWriteException
- Современный catch → getWriteResult и readonly writeResult
- Проверить ServerException как нынешнего родителя
- Старый 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 |
| Родитель ServerException | true |
| 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.
Полная самостоятельная программа и архив
<?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 команды.
- Два реальных отказа → свои типизированные результаты
- Выбрать code/слой + наблюдаемый status; проверить метки
- Сформировать согласованный массив отчёта без URI и trace
- HTML-таблица: INC-17 требует сверки, INC-18 исправления команды
Минимальная запись
$report = [
'incident'=>'INC-17', 'kind'=>'write_concern',
'code'=>$concern->getCode(), 'state'=>$observedStatus,
];
// Для оператора выбираем нужные поля; не публикуем URI, trace или полный getInfo сервера.Фрагмент показывает текущий шаг. Полная программа ниже содержит данные, настоящее обращение к MongoDB, проверки и очистку своей коллекции.
Результат
| Инцидент | Слой | Код | Наблюдаемое состояние | Следующее действие |
|---|---|---|---|---|
| INC-17 | write_concern | 100 | after | Сверить подтверждение; не повторять изменение |
| INC-18 | command | 59 | Не менялась этой командой | Исправить имя команды |
Почему получился такой результат
Код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 и атомарное изменение, отказы команд.
Источники и обсуждения
- WriteConcernError
- WriteConcernError::getCode
- WriteConcernError::getInfo
- WriteConcernError::getMessage
- WriteResult::getServer
- WriteResult::getWriteConcernError
- BulkWriteException::getWriteResult
- WriteException::getWriteResult (historical)
- CommandException::getResultDocument
- RuntimeException::hasErrorLabel
- BulkWriteException
- CommandException
- WriteException removal
- RuntimeException error labels
- WriteConcern constructor
- Manager::executeBulkWrite
- Write concern MongoDB8.0
- Retryable writes MongoDB8.0
- Error codes MongoDB8.0
- Replica set setup
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


