Skip to content

Commit 4eb2257

Browse files
committed
PHPLIB-935: Propagate Original Error for Write Errors Labeled NoWritesPerformed
1 parent 964e887 commit 4eb2257

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

tests/SpecTests/RetryableWritesSpecTest.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
namespace MongoDB\Tests\SpecTests;
44

5+
use MongoDB\Driver\Exception\BulkWriteException;
6+
use MongoDB\Driver\Monitoring\CommandFailedEvent;
7+
use MongoDB\Driver\Monitoring\CommandStartedEvent;
8+
use MongoDB\Driver\Monitoring\CommandSubscriber;
9+
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
510
use stdClass;
611

712
use function basename;
@@ -16,6 +21,9 @@
1621
*/
1722
class RetryableWritesSpecTest extends FunctionalTestCase
1823
{
24+
public const NOT_PRIMARY = 10107;
25+
public const SHUTDOWN_IN_PROGRESS = 91;
26+
1927
/**
2028
* Execute an individual test case from the specification.
2129
*
@@ -71,4 +79,79 @@ public function provideTests()
7179

7280
return $testArgs;
7381
}
82+
83+
/**
84+
* Prose test 1: when encountering a NoWritesPerformed error after an error with a RetryableWriteError label
85+
*/
86+
public function testRunForProseTest(): void
87+
{
88+
if (! $this->isReplicaSet()) {
89+
$this->markTestSkipped('Test only applies to replica sets');
90+
}
91+
92+
$client = self::createTestClient(null, ['retryWrites' => true]);
93+
94+
// Step 2: Configure a fail point with error code 91
95+
$this->configureFailPoint([
96+
'configureFailPoint' => 'failCommand',
97+
'mode' => ['times' => 1],
98+
'data' => [
99+
'writeConcernError' => [
100+
'code' => self::SHUTDOWN_IN_PROGRESS,
101+
'errorLabels' => ['RetryableWriteError'],
102+
],
103+
'failCommands' => ['insert'],
104+
],
105+
]);
106+
107+
$subscriber = new class ($this) implements CommandSubscriber {
108+
private $testCase;
109+
110+
public function __construct(FunctionalTestCase $testCase)
111+
{
112+
$this->testCase = $testCase;
113+
}
114+
115+
public function commandStarted(CommandStartedEvent $event): void
116+
{
117+
}
118+
119+
public function commandSucceeded(CommandSucceededEvent $event): void
120+
{
121+
if ($event->getCommandName() === 'insert') {
122+
// Step 3: Configure a fail point with code 10107
123+
$this->testCase->configureFailPoint([
124+
'configureFailPoint' => 'failCommand',
125+
'mode' => ['times' => 1],
126+
'data' => [
127+
'errorCode' => RetryableWritesSpecTest::NOT_PRIMARY,
128+
'errorLabels' => ['RetryableWriteError', 'NoWritesPerformed'],
129+
'failCommands' => ['insert'],
130+
],
131+
]);
132+
}
133+
}
134+
135+
public function commandFailed(CommandFailedEvent $event): void
136+
{
137+
}
138+
};
139+
140+
$client->getManager()->addSubscriber($subscriber);
141+
142+
// Step 4: Run insertOne
143+
try {
144+
$client->selectCollection('db', 'retryable_writes')->insertOne(['write' => 1]);
145+
} catch (BulkWriteException $e) {
146+
// Assert that the write concern error is from the first failpoint
147+
$this->assertSame(self::SHUTDOWN_IN_PROGRESS, $e->getWriteResult()->getWriteConcernError()->getCode());
148+
}
149+
150+
// Step 5: Disable the fail point
151+
$client->getManager()->removeSubscriber($subscriber);
152+
$this->configureFailPoint([
153+
'configureFailPoint' => 'failCommand',
154+
'mode' => 'off',
155+
]);
156+
}
74157
}

0 commit comments

Comments
 (0)