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
f86f2207
Unverified
Commit
f86f2207
authored
Aug 07, 2019
by
Andreas Braun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
PHPLIB-411: Fix resuming change stream if startAfter option was passed to original watch operation
parent
aac3c6bd
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
106 additions
and
4 deletions
+106
-4
ChangeStream.php
src/ChangeStream.php
+1
-1
Watch.php
src/Operation/Watch.php
+6
-3
WatchFunctionalTest.php
tests/Operation/WatchFunctionalTest.php
+99
-0
No files found.
src/ChangeStream.php
View file @
f86f2207
...
@@ -220,7 +220,7 @@ class ChangeStream implements Iterator
...
@@ -220,7 +220,7 @@ class ChangeStream implements Iterator
*/
*/
private
function
resume
()
private
function
resume
()
{
{
$this
->
iterator
=
call_user_func
(
$this
->
resumeCallable
,
$this
->
getResumeToken
());
$this
->
iterator
=
call_user_func
(
$this
->
resumeCallable
,
$this
->
getResumeToken
()
,
$this
->
hasAdvanced
);
$this
->
iterator
->
rewind
();
$this
->
iterator
->
rewind
();
$this
->
onIteration
(
$this
->
hasAdvanced
);
$this
->
onIteration
(
$this
->
hasAdvanced
);
...
...
src/Operation/Watch.php
View file @
f86f2207
...
@@ -250,7 +250,7 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
...
@@ -250,7 +250,7 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
{
{
return
new
ChangeStream
(
return
new
ChangeStream
(
$this
->
createChangeStreamIterator
(
$server
),
$this
->
createChangeStreamIterator
(
$server
),
function
(
$resumeToken
)
{
return
$this
->
resume
(
$resumeToken
);
}
function
(
$resumeToken
,
$hasAdvanced
)
{
return
$this
->
resume
(
$resumeToken
,
$hasAdvanced
);
}
);
);
}
}
...
@@ -333,10 +333,11 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
...
@@ -333,10 +333,11 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
*
*
* @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resume-process
* @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resume-process
* @param array|object|null $resumeToken
* @param array|object|null $resumeToken
* @param bool $hasAdvanced
* @return ChangeStreamIterator
* @return ChangeStreamIterator
* @throws InvalidArgumentException
* @throws InvalidArgumentException
*/
*/
private
function
resume
(
$resumeToken
=
null
)
private
function
resume
(
$resumeToken
=
null
,
$hasAdvanced
=
false
)
{
{
if
(
isset
(
$resumeToken
)
&&
!
is_array
(
$resumeToken
)
&&
!
is_object
(
$resumeToken
))
{
if
(
isset
(
$resumeToken
)
&&
!
is_array
(
$resumeToken
)
&&
!
is_object
(
$resumeToken
))
{
throw
InvalidArgumentException
::
invalidType
(
'$resumeToken'
,
$resumeToken
,
'array or object'
);
throw
InvalidArgumentException
::
invalidType
(
'$resumeToken'
,
$resumeToken
,
'array or object'
);
...
@@ -347,12 +348,14 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
...
@@ -347,12 +348,14 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
// Select a new server using the original read preference
// Select a new server using the original read preference
$server
=
$this
->
manager
->
selectServer
(
$this
->
aggregateOptions
[
'readPreference'
]);
$server
=
$this
->
manager
->
selectServer
(
$this
->
aggregateOptions
[
'readPreference'
]);
$resumeOption
=
isset
(
$this
->
changeStreamOptions
[
'startAfter'
])
&&
!
$hasAdvanced
?
'startAfter'
:
'resumeAfter'
;
unset
(
$this
->
changeStreamOptions
[
'resumeAfter'
]);
unset
(
$this
->
changeStreamOptions
[
'resumeAfter'
]);
unset
(
$this
->
changeStreamOptions
[
'startAfter'
]);
unset
(
$this
->
changeStreamOptions
[
'startAfter'
]);
unset
(
$this
->
changeStreamOptions
[
'startAtOperationTime'
]);
unset
(
$this
->
changeStreamOptions
[
'startAtOperationTime'
]);
if
(
$resumeToken
!==
null
)
{
if
(
$resumeToken
!==
null
)
{
$this
->
changeStreamOptions
[
'resumeAfter'
]
=
$resumeToken
;
$this
->
changeStreamOptions
[
$resumeOption
]
=
$resumeToken
;
}
}
if
(
$resumeToken
===
null
&&
$this
->
operationTime
!==
null
)
{
if
(
$resumeToken
===
null
&&
$this
->
operationTime
!==
null
)
{
...
...
tests/Operation/WatchFunctionalTest.php
View file @
f86f2207
...
@@ -1382,6 +1382,105 @@ class WatchFunctionalTest extends FunctionalTestCase
...
@@ -1382,6 +1382,105 @@ class WatchFunctionalTest extends FunctionalTestCase
$this
->
assertSame
(
$resumeToken
,
$changeStream
->
getResumeToken
());
$this
->
assertSame
(
$resumeToken
,
$changeStream
->
getResumeToken
());
}
}
/**
* Prose test: "$changeStream stage for ChangeStream started with startAfter
* against a server >=4.1.1 that has not received any results yet MUST
* include a startAfter option and MUST NOT include a resumeAfter option
* when resuming a change stream."
*/
public
function
testResumingChangeStreamWithoutPreviousResultsIncludesStartAfterOption
()
{
if
(
version_compare
(
$this
->
getServerVersion
(),
'4.1.1'
,
'<'
))
{
$this
->
markTestSkipped
(
'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'
);
}
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$this
->
defaultOptions
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$this
->
insertDocument
([
'x'
=>
1
]);
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$resumeToken
=
$changeStream
->
getResumeToken
();
$options
=
[
'startAfter'
=>
$resumeToken
]
+
$this
->
defaultOptions
;
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$options
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$changeStream
->
rewind
();
$this
->
killChangeStreamCursor
(
$changeStream
);
$aggregateCommand
=
null
;
(
new
CommandObserver
)
->
observe
(
function
()
use
(
$changeStream
)
{
$changeStream
->
next
();
},
function
(
array
$event
)
use
(
&
$aggregateCommand
)
{
if
(
$event
[
'started'
]
->
getCommandName
()
!==
'aggregate'
)
{
return
;
}
$aggregateCommand
=
$event
[
'started'
]
->
getCommand
();
}
);
$this
->
assertNotNull
(
$aggregateCommand
);
$this
->
assertObjectNotHasAttribute
(
'resumeAfter'
,
$aggregateCommand
->
pipeline
[
0
]
->
{
'$changeStream'
});
$this
->
assertObjectHasAttribute
(
'startAfter'
,
$aggregateCommand
->
pipeline
[
0
]
->
{
'$changeStream'
});
}
/**
* Prose test: "$changeStream stage for ChangeStream started with startAfter
* against a server >=4.1.1 that has received at least one result MUST
* include a resumeAfter option and MUST NOT include a startAfter option
* when resuming a change stream."
*/
public
function
testResumingChangeStreamWithPreviousResultsIncludesResumeAfterOption
()
{
if
(
version_compare
(
$this
->
getServerVersion
(),
'4.1.1'
,
'<'
))
{
$this
->
markTestSkipped
(
'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'
);
}
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$this
->
defaultOptions
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$this
->
insertDocument
([
'x'
=>
1
]);
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$resumeToken
=
$changeStream
->
getResumeToken
();
$options
=
[
'startAfter'
=>
$resumeToken
]
+
$this
->
defaultOptions
;
$operation
=
new
Watch
(
$this
->
manager
,
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
$options
);
$changeStream
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$changeStream
->
rewind
();
$this
->
insertDocument
([
'x'
=>
2
]);
$changeStream
->
next
();
$this
->
assertTrue
(
$changeStream
->
valid
());
$this
->
killChangeStreamCursor
(
$changeStream
);
$aggregateCommand
=
null
;
(
new
CommandObserver
)
->
observe
(
function
()
use
(
$changeStream
)
{
$changeStream
->
next
();
},
function
(
array
$event
)
use
(
&
$aggregateCommand
)
{
if
(
$event
[
'started'
]
->
getCommandName
()
!==
'aggregate'
)
{
return
;
}
$aggregateCommand
=
$event
[
'started'
]
->
getCommand
();
}
);
$this
->
assertNotNull
(
$aggregateCommand
);
$this
->
assertObjectNotHasAttribute
(
'startAfter'
,
$aggregateCommand
->
pipeline
[
0
]
->
{
'$changeStream'
});
$this
->
assertObjectHasAttribute
(
'resumeAfter'
,
$aggregateCommand
->
pipeline
[
0
]
->
{
'$changeStream'
});
}
private
function
assertNoCommandExecuted
(
callable
$callable
)
private
function
assertNoCommandExecuted
(
callable
$callable
)
{
{
$commands
=
[];
$commands
=
[];
...
...
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