Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

catching errors on streams #14304

Open
2 tasks done
simllll opened this issue Jan 29, 2024 · 4 comments
Open
2 tasks done

catching errors on streams #14304

simllll opened this issue Jan 29, 2024 · 4 comments
Labels
underlying library issue This issue is a bug with an underlying library, like the MongoDB driver or mongodb-core

Comments

@simllll
Copy link
Contributor

simllll commented Jan 29, 2024

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.1.1

Node.js version

20.x

MongoDB server version

6.x

Typescript version (if applicable)

5.1.6

Description

when using a mongoose stream, I cannot catch an initial error, it always kills the proess with an "uncaught exception".

following (pseudo) code:

const stream = someCollectionl.find().sort({'_id': -1}).stream({transform})
stream.on('error', err => console.error(err));

throws with

uncaughtException MongoServerError: Encountered non-retryable error during query :: caused by :: PlanExecutor error during aggregation :: caused by :: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting.
   at Connection.onMessage (/home/simon/Dev/hokify/hokify/node_modules/.pnpm/mongodb@6.3.0_snappy@7.2.2/node_modules/mongodb/src/cmap/connection.ts:421:18)
    at MessageStream.<anonymous> (/home/simon/Dev/hokify/hokify/node_modules/.pnpm/mongodb@6.3.0_snappy@7.2.2/node_modules/mongodb/src/cmap/connection.ts:251:56)
    at MessageStream.emit (node:events:517:28)
    at MessageStream.emit (node:domain:489:12)
    at processIncomingData (/home/simon/Dev/hokify/hokify/node_modules/.pnpm/mongodb@6.3.0_snappy@7.2.2/node_modules/mongodb/src/cmap/message_stream.ts:179:12)
    at MessageStream._write (/home/simon/Dev/hokify/hokify/node_modules/.pnpm/mongodb@6.3.0_snappy@7.2.2/node_modules/mongodb/src/cmap/message_stream.ts:68:5)
    at writeOrBuffer (node:internal/streams/writable:392:12)
    at _write (node:internal/streams/writable:333:10)
    at MessageStream.Writable.write (node:internal/streams/writable:337:10)
    at Socket.ondata (node:internal/streams/readable:777:22)
    at Socket.emit (node:events:517:28)
    at Socket.emit (node:domain:489:12)
    at addChunk (node:internal/streams/readable:335:12)
    at readableAddChunk (node:internal/streams/readable:308:9)
    at Socket.Readable.push (node:internal/streams/readable:245:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:190:23) {
  ok: 0,
  code: 292,
  codeName: 'QueryExceededMemoryLimitNoDiskUseAllowed',

I played around with it and couldn't find any way how to catch this error.

Steps to Reproduce

run a query that results into QueryExceededMemoryLimitNoDiskUseAllowed by simple using some big collection and putting a sort on it.

Expected Behavior

the error should be catchable.

@vkarpov15 vkarpov15 added this to the 8.1.2 milestone Feb 1, 2024
@vkarpov15 vkarpov15 added the needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue label Feb 1, 2024
@IslandRhythms IslandRhythms added can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. and removed needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue labels Feb 2, 2024
@IslandRhythms
Copy link
Collaborator

IslandRhythms commented Feb 2, 2024

const mongoose = require('mongoose');

const testSchema = new mongoose.Schema({
  studentId: String
});

const Test = mongoose.model('Test', testSchema);


async function run() {
  await mongoose.connect('mongodb://localhost:27017');
  const iterations = 1000000;
  console.log(`begin creating ${iterations} documents`)
  console.log(mongoose.connection.collections);
  for (let i = 0; i < iterations; i++) {
    await Test.create({ studentId: (''+i).repeat(100) });
  }

  console.log('run query');
  const stream = mongoose.connection.collection('tests').find().sort().stream();
  console.log('stream', stream);
  let i = 0;
  stream.on('data', () => { console.log('Doc', ++i); });
  stream.on('error', err => console.log(err));
  stream.on('end', () => console.log('Done'))
}

run();

@vkarpov15
Copy link
Collaborator

The issue is the transform. The following code prints "Caught" before the "Sort exceeded memory limit" error, which is expected:

const mongoose = require('mongoose');
const { Transform } = require('stream');
  
const testSchema = new mongoose.Schema({
  studentId: String
});

const Test = mongoose.model('Test', testSchema);


async function run() {
  await mongoose.connect('mongodb://localhost:27017');
  const iterations = 10000;
  /*console.log(`begin creating ${iterations} documents`)
  for (let i = 0; i < iterations; i++) {
    await Test.create({ studentId: ('' + i).repeat(5000) });
    console.log(i);
  }*/
  
  const transform = new Transform({
    transform(data, encoding, callback) {
      callback(null, data);
    },
  });
  
  console.log('run query');
  const stream = mongoose.connection.collection('tests').find().sort({ studentId: -1 }).stream(/*{ transform }*/);
  let i = 0;
  stream.on('data', () => { console.log('Doc', ++i); });
  stream.on('error', err => console.log('Caught', err));
  stream.on('end', () => console.log('Done'));
}

run();

However, uncomment the transform option and you get an uncaught error:

run query
node:events:492
      throw er; // Unhandled 'error' event
      ^

MongoServerError: Executor error during find command :: caused by :: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting.
...
Emitted 'error' event on ReadableCursorStream instance at:
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  ok: 0,
  code: 292,
  codeName: 'QueryExceededMemoryLimitNoDiskUseAllowed',
  [Symbol(errorLabels)]: Set(0) {}
}

Node.js v18.17.1

We're looking into how to fix this.

@vkarpov15 vkarpov15 added confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. underlying library issue This issue is a bug with an underlying library, like the MongoDB driver or mongodb-core and removed can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. labels Feb 5, 2024
@vkarpov15
Copy link
Collaborator

The issue looks like the underlying MongoDB node driver just uses pipe() to pass in the transform, which doesn't propagate errors. As a workaround, you can try the following:

  const stream = mongoose.connection.collection('tests').find().sort({ studentId: -1 }).stream(/*{ transform }*/);
  const transformed = stream.pipe(transform);
  let i = 0;
  transformed.on('data', () => { console.log('Doc', ++i); });
  transformed.on('error', err => console.log('Caught', err));
  transformed.on('end', () => console.log('Done'));
  stream.on('error', err => console.log('Caught', err));

@vkarpov15
Copy link
Collaborator

I opened PR in MongoDB Node driver repo, we will see if we can get that merged. In the meantime, use the workaround from my previous post. More info on transformed stream error handling here.

@vkarpov15 vkarpov15 removed the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Feb 5, 2024
@vkarpov15 vkarpov15 removed this from the 8.1.2 milestone Feb 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
underlying library issue This issue is a bug with an underlying library, like the MongoDB driver or mongodb-core
Projects
None yet
Development

No branches or pull requests

3 participants