|
8 | 8 | use MongoDB\Driver\Server;
|
9 | 9 | use MongoDB\Driver\Exception\ConnectionTimeoutException;
|
10 | 10 | use MongoDB\Exception\ResumeTokenException;
|
| 11 | +use MongoDB\Operation\CreateCollection; |
11 | 12 | use MongoDB\Operation\DatabaseCommand;
|
| 13 | +use MongoDB\Operation\DropCollection; |
12 | 14 | use MongoDB\Operation\InsertOne;
|
13 | 15 | use MongoDB\Operation\Watch;
|
14 | 16 | use MongoDB\Tests\CommandObserver;
|
@@ -588,6 +590,90 @@ public function testResumeTokenNotFoundAdvancesKey()
|
588 | 590 | $this->assertSame(2, $changeStream->key());
|
589 | 591 | }
|
590 | 592 |
|
| 593 | + public function testSessionPersistsAfterResume() |
| 594 | + { |
| 595 | + $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); |
| 596 | + |
| 597 | + $changeStream = null; |
| 598 | + $originalSession = null; |
| 599 | + $sessionAfterResume = []; |
| 600 | + $commands = []; |
| 601 | + |
| 602 | + /* We want to ensure that the lsid of the initial aggregate matches the |
| 603 | + * lsid of any aggregates after the change stream resumes. After |
| 604 | + * PHPC-1152 is complete, we will ensure that the lsid of the initial |
| 605 | + * aggregate matches the lsid of any subsequent aggregates and getMores. |
| 606 | + */ |
| 607 | + (new CommandObserver)->observe( |
| 608 | + function() use ($operation, &$changeStream) { |
| 609 | + $changeStream = $operation->execute($this->getPrimaryServer()); |
| 610 | + }, |
| 611 | + function($changeStream) use (&$originalSession) { |
| 612 | + if (isset($changeStream->aggregate)) { |
| 613 | + $originalSession = bin2hex((string) $changeStream->lsid->id); |
| 614 | + } |
| 615 | + } |
| 616 | + ); |
| 617 | + |
| 618 | + $changeStream->rewind(); |
| 619 | + $this->killChangeStreamCursor($changeStream); |
| 620 | + |
| 621 | + (new CommandObserver)->observe( |
| 622 | + function() use (&$changeStream) { |
| 623 | + $changeStream->next(); |
| 624 | + }, |
| 625 | + function ($changeStream) use (&$sessionAfterResume, &$commands) { |
| 626 | + $commands[] = key((array) $changeStream); |
| 627 | + $sessionAfterResume[] = bin2hex((string) $changeStream->lsid->id); |
| 628 | + } |
| 629 | + ); |
| 630 | + |
| 631 | + $expectedCommands = [ |
| 632 | + /* We expect a getMore to be issued because we are calling next(). */ |
| 633 | + 'getMore', |
| 634 | + /* Since we have killed the cursor, ChangeStream will resume by |
| 635 | + * issuing a new aggregate commmand. */ |
| 636 | + 'aggregate', |
| 637 | + /* When ChangeStream resumes, it overwrites its original cursor with |
| 638 | + * the new cursor resulting from the last aggregate command. This |
| 639 | + * removes the last reference to the old cursor, which causes the |
| 640 | + * driver to kill it (via mongoc_cursor_destroy()). */ |
| 641 | + 'killCursors', |
| 642 | + /* Finally, ChangeStream will rewind the new cursor as the last step |
| 643 | + * of the resume process. This results in one last getMore. */ |
| 644 | + 'getMore', |
| 645 | + ]; |
| 646 | + |
| 647 | + $this->assertSame($expectedCommands, $commands); |
| 648 | + |
| 649 | + foreach ($sessionAfterResume as $session) { |
| 650 | + $this->assertEquals($session, $originalSession); |
| 651 | + } |
| 652 | + } |
| 653 | + |
| 654 | + public function testSessionFreed() |
| 655 | + { |
| 656 | + $operation = new CreateCollection($this->getDatabaseName(), $this->getCollectionName()); |
| 657 | + $operation->execute($this->getPrimaryServer()); |
| 658 | + |
| 659 | + $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); |
| 660 | + $changeStream = $operation->execute($this->getPrimaryServer()); |
| 661 | + |
| 662 | + $rc = new ReflectionClass($changeStream); |
| 663 | + $rp = $rc->getProperty('resumeCallable'); |
| 664 | + $rp->setAccessible(true); |
| 665 | + |
| 666 | + $this->assertNotNull($rp->getValue($changeStream)); |
| 667 | + |
| 668 | + // Invalidate the cursor to verify that resumeCallable is unset when the cursor is exhausted. |
| 669 | + $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); |
| 670 | + $operation->execute($this->getPrimaryServer()); |
| 671 | + |
| 672 | + $changeStream->next(); |
| 673 | + |
| 674 | + $this->assertNull($rp->getValue($changeStream)); |
| 675 | + } |
| 676 | + |
591 | 677 | private function insertDocument($document)
|
592 | 678 | {
|
593 | 679 | $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document);
|
|
0 commit comments