Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
mongo-php-library
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sinan
mongo-php-library
Commits
516e56b5
Commit
516e56b5
authored
Jun 25, 2019
by
Jeremy Mikola
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'v1.4'
parents
aded75ea
b19328ac
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
134 additions
and
10 deletions
+134
-10
ChangeStream.php
src/ChangeStream.php
+24
-8
WatchFunctionalTest.php
tests/Operation/WatchFunctionalTest.php
+110
-2
No files found.
src/ChangeStream.php
View file @
516e56b5
...
...
@@ -50,6 +50,11 @@ class ChangeStream implements Iterator
private
$resumeCallable
;
private
$csIt
;
private
$key
=
0
;
/**
* Whether the change stream has advanced to its first result. This is used
* to determine whether $key should be incremented after an iteration event.
*/
private
$hasAdvanced
=
false
;
/**
...
...
@@ -103,7 +108,7 @@ class ChangeStream implements Iterator
{
try
{
$this
->
csIt
->
next
();
$this
->
onIteration
(
true
);
$this
->
onIteration
(
$this
->
hasAdvanced
);
}
catch
(
RuntimeException
$e
)
{
$this
->
resumeOrThrow
(
$e
);
}
...
...
@@ -118,6 +123,9 @@ class ChangeStream implements Iterator
{
try
{
$this
->
csIt
->
rewind
();
/* Unlike next() and resume(), the decision to increment the key
* does not depend on whether the change stream has advanced. This
* ensures that multiple calls to rewind() do not alter state. */
$this
->
onIteration
(
false
);
}
catch
(
RuntimeException
$e
)
{
$this
->
resumeOrThrow
(
$e
);
...
...
@@ -195,12 +203,12 @@ class ChangeStream implements Iterator
}
/**
* Perform housekeeping after an iteration event
(i.e. next or rewind)
.
* Perform housekeeping after an iteration event.
*
* @param boolean $i
sNext Whether the iteration event was a call to next()
* @param boolean $i
ncrementKey Increment $key if there is a current result
* @throws ResumeTokenException
*/
private
function
onIteration
(
$i
sNext
)
private
function
onIteration
(
$i
ncrementKey
)
{
/* If the cursorId is 0, the server has invalidated the cursor and we
* will never perform another getMore nor need to resume since any
...
...
@@ -212,15 +220,15 @@ class ChangeStream implements Iterator
$this
->
resumeCallable
=
null
;
}
/* Return early if there is not a current result. Avoid any attempt to
* increment the iterator's key or extract a resume token */
if
(
!
$this
->
valid
())
{
return
;
}
$this
->
resumeToken
=
$this
->
extractResumeToken
(
$this
->
csIt
->
current
());
/* Increment the key if the iteration event was a call to next() and we
* have already advanced past the first result. */
if
(
$isNext
&&
$this
->
hasAdvanced
)
{
if
(
$incrementKey
)
{
$this
->
key
++
;
}
...
...
@@ -237,7 +245,15 @@ class ChangeStream implements Iterator
$newChangeStream
=
call_user_func
(
$this
->
resumeCallable
,
$this
->
resumeToken
);
$this
->
csIt
=
$newChangeStream
->
csIt
;
$this
->
csIt
->
rewind
();
$this
->
onIteration
(
false
);
/* Note: if we are resuming after a call to ChangeStream::rewind(),
* $hasAdvanced will always be false. For it to be true, rewind() would
* need to have thrown a RuntimeException with a resumable error, which
* can only happen during the first call to IteratorIterator::rewind()
* before onIteration() has a chance to set $hasAdvanced to true.
* Otherwise, IteratorIterator::rewind() would either NOP (consecutive
* rewinds) or throw a LogicException (rewind after next), neither of
* which would result in a call to resume(). */
$this
->
onIteration
(
$this
->
hasAdvanced
);
}
/**
...
...
tests/Operation/WatchFunctionalTest.php
View file @
516e56b5
...
...
@@ -8,6 +8,7 @@ use MongoDB\Driver\Manager;
use
MongoDB\Driver\ReadPreference
;
use
MongoDB\Driver\Server
;
use
MongoDB\Driver\Exception\ConnectionTimeoutException
;
use
MongoDB\Driver\Exception\LogicException
;
use
MongoDB\Exception\ResumeTokenException
;
use
MongoDB\Operation\DatabaseCommand
;
use
MongoDB\Operation\InsertOne
;
...
...
@@ -217,6 +218,62 @@ class WatchFunctionalTest extends FunctionalTestCase
$this
->
assertEquals
(
$expectedOperationTime
,
$command
->
pipeline
[
0
]
->
{
'$changeStream'
}
->
startAtOperationTime
);
}
public
function
testRewindMultipleTimesWithResults
()
{
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$this
->
defaultOptions
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$this
->
insertDocument
([
'x'
=>
1
]);
$this
->
insertDocument
([
'x'
=>
2
]);
$changeStream
->
rewind
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
0
,
$changeStream
->
key
());
$this
->
assertNotNull
(
$changeStream
->
current
());
// Subsequent rewind does not change iterator state
$changeStream
->
rewind
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
0
,
$changeStream
->
key
());
$this
->
assertNotNull
(
$changeStream
->
current
());
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
1
,
$changeStream
->
key
());
$this
->
assertNotNull
(
$changeStream
->
current
());
// Rewinding after advancing the iterator is an error
$this
->
expectException
(
LogicException
::
class
);
$changeStream
->
rewind
();
}
public
function
testRewindMultipleTimesWithNoResults
()
{
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$this
->
defaultOptions
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$changeStream
->
rewind
();
$this
->
assertFalse
(
$changeStream
->
valid
());
$this
->
assertNull
(
$changeStream
->
key
());
$this
->
assertNull
(
$changeStream
->
current
());
// Subsequent rewind does not change iterator state
$changeStream
->
rewind
();
$this
->
assertFalse
(
$changeStream
->
valid
());
$this
->
assertNull
(
$changeStream
->
key
());
$this
->
assertNull
(
$changeStream
->
current
());
$changeStream
->
next
();
$this
->
assertFalse
(
$changeStream
->
valid
());
$this
->
assertNull
(
$changeStream
->
key
());
$this
->
assertNull
(
$changeStream
->
current
());
// Rewinding after advancing the iterator is an error
$this
->
expectException
(
LogicException
::
class
);
$changeStream
->
rewind
();
}
public
function
testRewindResumesAfterConnectionException
()
{
/* In order to trigger a dropped connection, we'll use a new client with
...
...
@@ -314,20 +371,67 @@ class WatchFunctionalTest extends FunctionalTestCase
$this
->
assertMatchesDocument
(
$expectedResult
,
$changeStream
->
current
());
}
public
function
testResume
TokenIsUpdatedAfterResuming
()
public
function
testResume
MultipleTimesInSuccession
()
{
$this
->
insertDocument
([
'_id'
=>
1
]);
$operation
=
new
CreateCollection
(
$this
->
getDatabaseName
(),
$this
->
getCollectionName
());
$operation
->
execute
(
$this
->
getPrimaryServer
());
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$this
->
defaultOptions
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
/* Killing the cursor when there are no results will test that neither
* the initial rewind() nor its resume attempt incremented the key. */
$this
->
killChangeStreamCursor
(
$changeStream
);
$changeStream
->
rewind
();
$this
->
assertFalse
(
$changeStream
->
valid
());
$this
->
assertNull
(
$changeStream
->
key
());
$this
->
assertNull
(
$changeStream
->
current
());
$this
->
insertDocument
([
'_id'
=>
1
]);
/* Killing the cursor a second time when there is a result will test
* that the resume attempt picks up the latest change. */
$this
->
killChangeStreamCursor
(
$changeStream
);
$changeStream
->
rewind
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
0
,
$changeStream
->
key
());
$expectedResult
=
[
'_id'
=>
$changeStream
->
current
()
->
_id
,
'operationType'
=>
'insert'
,
'fullDocument'
=>
[
'_id'
=>
1
],
'ns'
=>
[
'db'
=>
$this
->
getDatabaseName
(),
'coll'
=>
$this
->
getCollectionName
()],
'documentKey'
=>
[
'_id'
=>
1
],
];
$this
->
assertMatchesDocument
(
$expectedResult
,
$changeStream
->
current
());
/* Killing the cursor a second time will not trigger a resume until
* ChangeStream::next() is called. A successive call to rewind() should
* not change the iterator's state and preserve the current result. */
$this
->
killChangeStreamCursor
(
$changeStream
);
$changeStream
->
rewind
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
0
,
$changeStream
->
key
());
$expectedResult
=
[
'_id'
=>
$changeStream
->
current
()
->
_id
,
'operationType'
=>
'insert'
,
'fullDocument'
=>
[
'_id'
=>
1
],
'ns'
=>
[
'db'
=>
$this
->
getDatabaseName
(),
'coll'
=>
$this
->
getCollectionName
()],
'documentKey'
=>
[
'_id'
=>
1
],
];
$this
->
assertMatchesDocument
(
$expectedResult
,
$changeStream
->
current
());
$this
->
insertDocument
([
'_id'
=>
2
]);
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
1
,
$changeStream
->
key
());
$expectedResult
=
[
'_id'
=>
$changeStream
->
current
()
->
_id
,
...
...
@@ -345,6 +449,7 @@ class WatchFunctionalTest extends FunctionalTestCase
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
2
,
$changeStream
->
key
());
$expectedResult
=
[
'_id'
=>
$changeStream
->
current
()
->
_id
,
...
...
@@ -366,6 +471,7 @@ class WatchFunctionalTest extends FunctionalTestCase
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
assertSame
(
3
,
$changeStream
->
key
());
$expectedResult
=
[
'_id'
=>
$changeStream
->
current
()
->
_id
,
...
...
@@ -697,6 +803,8 @@ class WatchFunctionalTest extends FunctionalTestCase
$this
->
insertDocument
([
'x'
=>
1
]);
$this
->
insertDocument
([
'x'
=>
2
]);
/* Note: we intentionally do not start iteration with rewind() to ensure
* that next() behaves identically when called without rewind(). */
$changeStream
->
next
();
$this
->
assertSame
(
0
,
$changeStream
->
key
());
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment