question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Bulk helper semaphore handling results in hanging await for long-running requests (exceeding flushInterval)

See original GitHub issue

🐛 Bug Report

Bulk helper hangs forever when flushInterval is exceeded while iterator is already awaiting semaphore.

To Reproduce

Steps to reproduce the behavior:

  1. Run below code against a test cluster - should complete successfully
  2. Simulate long running server-side operation exceeding configured flushInterval. Multiple ways to do this but one way is to modify the compiled Helpers.js with the following:
client.bulk(Object.assign({}, bulkOptions, { body: bulkBody }), reqOptions, async (err, { body }) => {
await new Promise(resolve => setTimeout(resolve, flushInterval));
if (err) return callback(err, null)
  1. Re-run below code and watch the hanging await caused by onFlushTimeout() invoked on a payload already awaiting semaphore()

Paste your code here:

'use strict'
const util = require("util")
const { Readable } = require('stream');
const { Client } = require('@elastic/elasticsearch');

async function* generator() {
    let i = 0
    while (i < 10) {
        await new Promise(resolve => setTimeout(resolve, 1 * 1000));
        yield { i: i++ };
    }
};

const readableStream = Readable.from(generator());

const elasticClient = new Client({
    [TESTCLUSTER]
});

(async () => {
    const bulkHelper = elasticClient.helpers.bulk({
        flushBytes: 43,
        concurrency: 1,
        datasource: readableStream,
        onDocument(doc) {
            console.log(doc)
            return {
                index: { _index: 'semaphoretest' }
            }
        },
        onDrop(doc) {
            console.log(doc);
        }
    }).catch((err) => {
        console.error(err);
    });

    while (util.inspect(bulkHelper).includes('pending')) {
        await new Promise(resolve => setTimeout(resolve, 1 * 1000));
        console.log('...waiting');
    }

    console.log(await bulkHelper);
})();

Expected behavior

Bulk helper awaits gracefully for queued requests to complete, error, or timeout.

Paste the results here:

{ i: 0 }
...waiting
{ i: 1 }
...waiting
...waiting
...waiting
...[forever]

Your Environment

  • node version: v14.17.6
  • @elastic/elasticsearch version: >=7.15.0
  • os: Linux

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
delvedorcommented, May 24, 2022

Heya, I wrote this to reproduce the issue:

const dataset = [
  { user: 'jon', age: 23 },
  { user: 'arya', age: 18 },
  { user: 'tyrion', age: 39 }
]

test('issue #1562', async t => {
  async function handler (req, res) {
    console.log(req.url)
    setTimeout(() => {
      res.writeHead(200, { 'content-type': 'application/json' })
      res.end(JSON.stringify({ errors: false, items: [{}] }))
    }, 1000)
  }

  const [{ port }, server] = await buildServer(handler)
  const client = new Client({ node: `http://localhost:${port}` })

  async function * generator () {
    const data = dataset.slice()
    for (const doc of data) {
      await sleep(1000)
      yield doc
    }
  }

  const result = await client.helpers.bulk({
    datasource: Readable.from(generator()),
    flushBytes: 1,
    flushInterval: 1000,
    concurrency: 1,
    onDocument (doc) {
      return {
        index: { _index: 'test' }
      }
    },
    onDrop (doc) {
      t.fail('This should never be called')
    }
  })

  t.type(result.time, 'number')
  t.type(result.bytes, 'number')
  t.match(result, {
    total: 3,
    successful: 3,
    retry: 0,
    failed: 0,
    aborted: false
  })

  server.stop()
})

My observations:

  • if the flushInterval is exactly the same as the server timeout, the third request is never being sent
  • if the flushInterval is different from the server timeout (doesn’t matter if higher or lower), the code works as expected.

It happens both in v7 and v8. This is weird, we’ll investigate. Thank you for reporting!

0reactions
YoannMacommented, Apr 27, 2022

(comment to avoid stale)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Reference Manual: Configuration Parameters - SAP Help Portal
When a user logs off SAP ASE, or when the value of cpu accounting flush interval is exceeded, the accumulated CPU usage statistics...
Read more >
Java application using BulkProcessing hangs if elasticsearch ...
Java application hangs after some time and becomes unresponsive. We are indexing data using ... This thread is waiting to acquire semaphore.
Read more >
Oracle VM VirtualBox User Manual - Software Download
Welcome to Oracle VM VirtualBox! VirtualBox is a cross-platform virtualization application. What does that mean? For one thing,.
Read more >
Oracle VM VirtualBox User Manual
Launching more than 120 VMs on Solaris hosts . ... When dealing with virtualization (and also for understanding the following chapters of ...
Read more >
SAP Adaptive Server Enterprise的所有已知BUG列表(1)
cancel command) may result in ASE hang. ... then the new request will wait for the existing request without generating a warning message...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found