Skip to content

Make a failed server cursor read reject instead of ending the cursor - #13

Draft
mark1russell7 wants to merge 1 commit into
develfrom
fix/cursor-read-errors
Draft

mark1russell7 wants to merge 1 commit into
develfrom
fix/cursor-read-errors

Conversation

@mark1russell7

Copy link
Copy Markdown

Summary

Fixes meteor#14794.

Since 3.3.1 (meteor#13786), AsynchronousCursor._rawNextObjectPromise awaits next() inside a try that only logs. A read that fails ends the cursor as if it had no more documents. On the server, fetchAsync resolves with the documents read so far, often [], findOneAsync resolves with undefined, and forEachAsync stops early. A caller can't tell a failed read from an empty result.

Meteor's own callers already handle these errors, but never get them:

  • The oplog and change-stream drivers retry their first query after a network error, and stop the observe after a server error.
  • The polling driver reports a failed first poll through queryError.
  • tail() restarts its cursor.

Changes

  • _rawNextObjectPromise rethrows the error. A next() that fails while the cursor is closing still ends the cursor, because close() waits for the pending next().
  • _nextObjectPromiseWithTimeout cancels its timer with then(cancel, cancel) instead of finally(). finally() returned a second promise that rejected with the same error and had no handler, so every failed timed read became an unhandled rejection.
  • New server tests in tests/asynchronous_cursor_tests.js.

Verification

The mongo package's server tests (test-server-tests-in-console-once), run twice:

  • With the fix reverted: 4 of the 5 new tests fail ("did not throw an error as expected"), and the other 232 pass. The fifth test, for a next() that fails while the cursor closes, passes either way: it guards behavior that close() already has.
  • With the fix: 236 of 236 pass.

Since 3.3.1, AsynchronousCursor._rawNextObjectPromise awaited next()
inside a try that only logged the error. A read that failed ended the
cursor as if it had no more documents, so fetchAsync resolved with the
documents read so far, findOneAsync with undefined, and forEachAsync
stopped early.

_rawNextObjectPromise now rethrows the error. A next() that fails while
the cursor is closing still ends the cursor, because close() waits for
the pending next().

_nextObjectPromiseWithTimeout cancels its timer with then(cancel,
cancel) instead of finally(). finally() returned a second promise that
rejected with the same error and had no handler.

Fixes meteor#14794.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant