|
2 | 2 |
|
3 | 3 | namespace MongoDB\Tests;
|
4 | 4 |
|
| 5 | +use MongoDB\Client; |
5 | 6 | use MongoDB\Database;
|
6 | 7 | use MongoDB\Driver\Cursor;
|
7 | 8 | use MongoDB\Driver\Server;
|
@@ -1180,6 +1181,245 @@ public function testIndex_example_2()
|
1180 | 1181 | $this->assertEquals('cuisine_1_name_1', $indexName);
|
1181 | 1182 | }
|
1182 | 1183 |
|
| 1184 | + // Start Transactions Intro Example 1 |
| 1185 | + private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Session $session) |
| 1186 | + { |
| 1187 | + $session->startTransaction([ |
| 1188 | + 'readConcern' => new \MongoDB\Driver\ReadConcern('snapshot'), |
| 1189 | + 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY) |
| 1190 | + ]); |
| 1191 | + |
| 1192 | + try { |
| 1193 | + $client->hr->employees->updateOne( |
| 1194 | + ['employee' => 3], |
| 1195 | + ['$set' => ['status' => 'Inactive']], |
| 1196 | + ['session' => $session] |
| 1197 | + ); |
| 1198 | + $client->reporting->events->insertOne( |
| 1199 | + ['employee' => 3, 'status' => [ 'new' => 'Inactive', 'old' => 'Active']], |
| 1200 | + ['session' => $session] |
| 1201 | + ); |
| 1202 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1203 | + echo "Caught exception during transaction, aborting.\n"; |
| 1204 | + $session->abortTransaction(); |
| 1205 | + throw $error; |
| 1206 | + } |
| 1207 | + |
| 1208 | + while (true) { |
| 1209 | + try { |
| 1210 | + $session->commitTransaction(); |
| 1211 | + echo "Transaction committed.\n"; |
| 1212 | + break; |
| 1213 | + } catch (\MongoDB\Driver\Exception\CommandException $error) { |
| 1214 | + $resultDoc = $error->getResultDocument(); |
| 1215 | + |
| 1216 | + if (isset($resultDoc->errorLabels) && in_array('UnknownTransactionCommitResult', $resultDoc->errorLabels)) { |
| 1217 | + echo "UnknownTransactionCommitResult, retrying commit operation ...\n"; |
| 1218 | + continue; |
| 1219 | + } else { |
| 1220 | + echo "Error during commit ...\n"; |
| 1221 | + throw $error; |
| 1222 | + } |
| 1223 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1224 | + echo "Error during commit ...\n"; |
| 1225 | + throw $error; |
| 1226 | + } |
| 1227 | + } |
| 1228 | + } |
| 1229 | + // End Transactions Intro Example 1 |
| 1230 | + |
| 1231 | + public function testTransactions_intro_example_1() |
| 1232 | + { |
| 1233 | + if ($this->getPrimaryServer()->getType() === Server::TYPE_STANDALONE) { |
| 1234 | + $this->markTestSkipped('Transactions are not supported on standalone servers'); |
| 1235 | + } |
| 1236 | + if (version_compare($this->getFeatureCompatibilityVersion(), '4.0', '<')) { |
| 1237 | + $this->markTestSkipped('Transactions are only supported on FCV 4.0 or higher'); |
| 1238 | + } |
| 1239 | + |
| 1240 | + $client = new Client($this->getUri()); |
| 1241 | + |
| 1242 | + /* The WC is required: https://docs.mongodb.com/manual/core/transactions/#transactions-and-locks */ |
| 1243 | + $client->hr->dropCollection('employees', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1244 | + $client->reporting->dropCollection('events', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1245 | + |
| 1246 | + /* Collections need to be created before a transaction starts */ |
| 1247 | + $client->hr->createCollection('employees', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1248 | + $client->reporting->createCollection('events', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1249 | + |
| 1250 | + $session = $client->startSession(); |
| 1251 | + |
| 1252 | + ob_start(); |
| 1253 | + try { |
| 1254 | + $this->updateEmployeeInfo1($client, $session); |
| 1255 | + } finally { |
| 1256 | + ob_end_clean(); |
| 1257 | + } |
| 1258 | + } |
| 1259 | + |
| 1260 | + // Start Transactions Retry Example 1 |
| 1261 | + private function runTransactionWithRetry1(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session) |
| 1262 | + { |
| 1263 | + while (true) { |
| 1264 | + try { |
| 1265 | + $txnFunc($client, $session); // performs transaction |
| 1266 | + break; |
| 1267 | + } catch (\MongoDB\Driver\Exception\CommandException $error) { |
| 1268 | + $resultDoc = $error->getResultDocument(); |
| 1269 | + echo "Transaction aborted. Caught exception during transaction.\n"; |
| 1270 | + |
| 1271 | + // If transient error, retry the whole transaction |
| 1272 | + if (isset($resultDoc->errorLabels) && in_array('TransientTransactionError', $resultDoc->errorLabels)) { |
| 1273 | + echo "TransientTransactionError, retrying transaction ...\n"; |
| 1274 | + continue; |
| 1275 | + } else { |
| 1276 | + throw $error; |
| 1277 | + } |
| 1278 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1279 | + throw $error; |
| 1280 | + } |
| 1281 | + } |
| 1282 | + } |
| 1283 | + // End Transactions Retry Example 1 |
| 1284 | + |
| 1285 | + // Start Transactions Retry Example 2 |
| 1286 | + private function commitWithRetry2(\MongoDB\Driver\Session $session) |
| 1287 | + { |
| 1288 | + while (true) { |
| 1289 | + try { |
| 1290 | + $session->commitTransaction(); |
| 1291 | + echo "Transaction committed.\n"; |
| 1292 | + break; |
| 1293 | + } catch (\MongoDB\Driver\Exception\CommandException $error) { |
| 1294 | + $resultDoc = $error->getResultDocument(); |
| 1295 | + |
| 1296 | + if (isset($resultDoc->errorLabels) && in_array('UnknownTransactionCommitResult', $resultDoc->errorLabels)) { |
| 1297 | + echo "UnknownTransactionCommitResult, retrying commit operation ...\n"; |
| 1298 | + continue; |
| 1299 | + } else { |
| 1300 | + echo "Error during commit ...\n"; |
| 1301 | + throw $error; |
| 1302 | + } |
| 1303 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1304 | + echo "Error during commit ...\n"; |
| 1305 | + throw $error; |
| 1306 | + } |
| 1307 | + } |
| 1308 | + } |
| 1309 | + // End Transactions Retry Example 2 |
| 1310 | + |
| 1311 | + // Start Transactions Retry Example 3 |
| 1312 | + private function runTransactionWithRetry3(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session) |
| 1313 | + { |
| 1314 | + while (true) { |
| 1315 | + try { |
| 1316 | + $txnFunc($client, $session); // performs transaction |
| 1317 | + break; |
| 1318 | + } catch (\MongoDB\Driver\Exception\CommandException $error) { |
| 1319 | + $resultDoc = $error->getResultDocument(); |
| 1320 | + |
| 1321 | + // If transient error, retry the whole transaction |
| 1322 | + if (isset($resultDoc->errorLabels) && in_array('TransientTransactionError', $resultDoc->errorLabels)) { |
| 1323 | + continue; |
| 1324 | + } else { |
| 1325 | + throw $error; |
| 1326 | + } |
| 1327 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1328 | + throw $error; |
| 1329 | + } |
| 1330 | + } |
| 1331 | + } |
| 1332 | + |
| 1333 | + private function commitWithRetry3(\MongoDB\Driver\Session $session) |
| 1334 | + { |
| 1335 | + while (true) { |
| 1336 | + try { |
| 1337 | + $session->commitTransaction(); |
| 1338 | + echo "Transaction committed.\n"; |
| 1339 | + break; |
| 1340 | + } catch (\MongoDB\Driver\Exception\CommandException $error) { |
| 1341 | + $resultDoc = $error->getResultDocument(); |
| 1342 | + |
| 1343 | + if (isset($resultDoc->errorLabels) && in_array('UnknownTransactionCommitResult', $resultDoc->errorLabels)) { |
| 1344 | + echo "UnknownTransactionCommitResult, retrying commit operation ...\n"; |
| 1345 | + continue; |
| 1346 | + } else { |
| 1347 | + echo "Error during commit ...\n"; |
| 1348 | + throw $error; |
| 1349 | + } |
| 1350 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1351 | + echo "Error during commit ...\n"; |
| 1352 | + throw $error; |
| 1353 | + } |
| 1354 | + } |
| 1355 | + } |
| 1356 | + |
| 1357 | + private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Session $session) |
| 1358 | + { |
| 1359 | + $session->startTransaction([ |
| 1360 | + 'readConcern' => new \MongoDB\Driver\ReadConcern("snapshot"), |
| 1361 | + 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY) |
| 1362 | + ]); |
| 1363 | + |
| 1364 | + try { |
| 1365 | + $client->hr->employees->updateOne( |
| 1366 | + ['employee' => 3], |
| 1367 | + ['$set' => ['status' => 'Inactive']], |
| 1368 | + ['session' => $session] |
| 1369 | + ); |
| 1370 | + $client->reporting->events->insertOne( |
| 1371 | + ['employee' => 3, 'status' => [ 'new' => 'Inactive', 'old' => 'Active']], |
| 1372 | + ['session' => $session] |
| 1373 | + ); |
| 1374 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1375 | + echo "Caught exception during transaction, aborting.\n"; |
| 1376 | + $session->abortTransaction(); |
| 1377 | + throw $error; |
| 1378 | + } |
| 1379 | + |
| 1380 | + $this->commitWithRetry3($session); |
| 1381 | + } |
| 1382 | + |
| 1383 | + private function doUpdateEmployeeInfo(\MongoDB\Client $client) |
| 1384 | + { |
| 1385 | + // Start a session. |
| 1386 | + $session = $client->startSession(); |
| 1387 | + |
| 1388 | + try { |
| 1389 | + $this->runTransactionWithRetry3([$this, 'updateEmployeeInfo3'], $client, $session); |
| 1390 | + } catch (\MongoDB\Driver\Exception\Exception $error) { |
| 1391 | + // Do something with error |
| 1392 | + } |
| 1393 | + } |
| 1394 | + // End Transactions Retry Example 3 |
| 1395 | + |
| 1396 | + public function testTransactions_retry_example_3() |
| 1397 | + { |
| 1398 | + if ($this->getPrimaryServer()->getType() === Server::TYPE_STANDALONE) { |
| 1399 | + $this->markTestSkipped('Transactions are not supported on standalone servers'); |
| 1400 | + } |
| 1401 | + if (version_compare($this->getFeatureCompatibilityVersion(), '4.0', '<')) { |
| 1402 | + $this->markTestSkipped('Transactions are only supported on FCV 4.0 or higher'); |
| 1403 | + } |
| 1404 | + |
| 1405 | + $client = new Client($this->getUri()); |
| 1406 | + |
| 1407 | + /* The WC is required: https://docs.mongodb.com/manual/core/transactions/#transactions-and-locks */ |
| 1408 | + $client->hr->dropCollection('employees', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1409 | + $client->reporting->dropCollection('events', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1410 | + |
| 1411 | + /* Collections need to be created before a transaction starts */ |
| 1412 | + $client->hr->createCollection('employees', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1413 | + $client->reporting->createCollection('events', ['writeConcern' => new \MongoDB\Driver\WriteConcern('majority')]); |
| 1414 | + |
| 1415 | + ob_start(); |
| 1416 | + try { |
| 1417 | + $this->doUpdateEmployeeInfo($client); |
| 1418 | + } finally { |
| 1419 | + ob_end_clean(); |
| 1420 | + } |
| 1421 | + } |
| 1422 | + |
1183 | 1423 | /**
|
1184 | 1424 | * Return the test collection name.
|
1185 | 1425 | *
|
|
0 commit comments