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

consider setTimeout instead of setInterval for polling #164

Closed
2 tasks done
cmawhorter opened this issue May 30, 2022 · 13 comments · Fixed by #174
Closed
2 tasks done

consider setTimeout instead of setInterval for polling #164

cmawhorter opened this issue May 30, 2022 · 13 comments · Fixed by #174

Comments

@cmawhorter
Copy link

Prerequisites

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

Issue

setInterval will repeatedly call at the scheduled time whether or not the previous call has finished. if the server is already under load, this will likely add to the problem, because those setInterval calls will start to stack. i imagine this would be especially bad in node v8 with setInterval(..., 5ms).

for node >= v10.2.0, using setTimeout and timer.refresh would avoid this issue. i swapped it in and tests pass in node v14, at least:

  const timer = setTimeout(() => {
    updateMemoryUsage()
    timer.refresh()
  }, sampleInterval)
@mcollina
Copy link
Member

Good spot indeed!
Would you like to send a Pull Request to address this issue? Remember to add unit tests.

@davideroffo
Copy link
Contributor

While working on fixing this problem, I noticed that the behaviour @cmawhorter mentioned does not occur with setInterval.
If you check this GIST: https://gist.github.com/davideroffo/b4e2adcc7ebb9e1fb431acb318a7aa45 you will notice that even if the event-loop is blocked manually, the function called by setInterval does not accumulate in the queue.

Could you provide a reproducible test for this @cmawhorter?

The only thing I noticed and that can be improved using setTimeout is the custom health check, where we can address this issue: https://developer.mozilla.org/en-US/docs/Web/API/setInterval#ensure_that_execution_duration_is_shorter_than_interval_frequency if healthCheck provided the user is taking longer than the programmed time interval.

@simoneb
Copy link
Contributor

simoneb commented Aug 23, 2022

@cmawhorter can you provide a repro? We couldn't reproduce the issue, so we assume the issue doesn't actually happen.

@cmawhorter
Copy link
Author

the most important thing here is probably the async healthcheck option. the built-in memory check might be a problem on node v8 with underpowered hardware, but that'd depend on how many ms eventLoopUtilization().utilization takes to calculate. i bet it'd rarely take > 5ms, so probably an edge case for that one.

healthcheck is the primary concern here unless the user is careful. the example in the readme will fall down if checking the db connectivity takes longer than 500ms e.g. it times out. AFAIK return order is essentially random when setInterval calls stack like that, and the healthcheck could show the db is up when it's not or visa versa.

personally, i treat setInterval like eval as a no-go except for very limited situations.

@mcollina
Copy link
Member

@cmawhorter can you provide a test case? So that we know this is fixed and we don't regress.

@simoneb
Copy link
Contributor

simoneb commented Aug 24, 2022

@cmawhorter as we explained earlier, setInterval doesn't behave the way you imply it behaves.

Try out this snippet of code.

setInterval(() => { 
    console.log('interval triggered on', new Date())
    
    const now = new Date(); 
    now.setSeconds(now.getSeconds() + 2);

    console.time('sleeping duration') 
    
    while(new Date() - now) {} 

    console.timeEnd('sleeping duration')     
}, 1000)

You will see that despite the interval being scheduled to run every 1 second, the body of the callback function blocks for 2 seconds.

What you suggest is that there are overlapping executions of the callback, whereas there aren't. The callback function is executed approximately every 2 seconds instead

@mcollina
Copy link
Member

@simoneb I think you need to add some asynchronous action inside the setInterval(). If a db healtcheck takes more than the interval, we will fire it twice.

@climba03003
Copy link
Member

climba03003 commented Aug 24, 2022

@simoneb The below one should illustrate the problem.

import { setTimeout } from 'timers/promises'


setInterval(async () => { 
  const now = Date.now()
  console.log('interval triggered on', new Date(now))

  console.time('sleeping duration ' + now) 
  
  await setTimeout(Math.random() * 2000)

  console.timeEnd('sleeping duration ' + now)
}, 1000)

@simoneb
Copy link
Contributor

simoneb commented Aug 24, 2022

It would be a lot easier if the OP included a repro so we don't need to guess. Anyway, the PR is ready now. Testing this is particularly cumbersome

@simoneb simoneb self-assigned this Aug 24, 2022
@mcollina
Copy link
Member

Indeed on all accounts @simoneb!

@cmawhorter
Copy link
Author

apologies for the lack of repro. i ended up going a different route than the plugin and mainly posted the issue as a psa

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 24, 2022

@cmawhorter

Well better than not reporting. ;)

btw. Its strage. I remember using your project named FLIR about a decade ago. I DM'ed you on twitter.

@cmawhorter
Copy link
Author

@Uzlopak lol. holy crap... that's a lifetime ago. good to hear from you again man!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants