Will setTimeout use Heroku free dyno hours? - javascript

I have a bot with a command that allows the user to input a message separated with a dash and then a specified time, this is then passed to the bot and the bot reminds the user with the message after the specified amount of time has passed.
function reminder(msg) {
const message = msg.content.replace(this.prefix+this.name+" ","");
const params = message.split("-");
setTimeout(() => {
msg.channel.sendMessage(params[0]);
}, (parseInt(params[1])*1000));
}
I intend to run this bot on Heroku, but since I'm just a teenager and this is a hobby for me I have to use the free dyno hours that Heroku gives me every month. If someone uses this function of the bot will the timing mechanism of setTimeout keep my dynos enabled and use the free dyno hours?
Edit: If you believe there is a better free alternative to Heroku that any of you know of that would be great :)

Yes and no.
Let's assume the logic in setTimeout will be running after every less than 30 minutes, so YES the heroku server will be still awake, so it'll be using the free dyno hours.
But if the message to be sent after more than 30 minutes, let's say one hour, so if there is no any requests hitting your server in this period , your server will sleep after 30m of inactivity, so NO as long as no incoming requests.
Read more about Heroku free dyno hours, here.

You can try this: http://neversleep.ml
It watches your server and will send notification emails if it crashes.

Related

JavaScript unhackable countdown timer

I'm currently working on a quizz app. When a question appears the users has 10 seconds to answer it, otherwise they don't get the points for that question. Once the timer is up, I want to autmatically move to the next question. I am currently facing issues on how to make the 10 second countdown timer "unhackable" by the client.
My initial idea was to use something along the lines of setTimeout() on the client-side for 10 seconds, and once the timer is complete, ask the server to fetch the next question. The problem with this is that the client-side timer can be hacked/modified to run for longer than 10 seconds, potentionally giving some users longer than 10 seconds to answer the question.
client <--- sends question --- server
|
start timer for 10 seconds (as this is client-side, it could easily be extended)
|
.
10 seconds later
.
V
client --- ask for next question / send answer ---> server
In order to keep it unhackable, I thought of moving the time checking logic to the sever-side. This would involve keeping two variables (A and B) on the server-side per connected user, one representing the time the question was sent, and the other representing the time an answer was given. The client-side timer would still run, except the server side uses the time-stamps to perform some validation to check if the difference between the timestamps A and B exceeds 10 seconds:
client <--- sends question --- server (send question at timestamp `A`)
|
start timer for 10 seconds (as this is client-side, it could easily be extended)
|
.
10 seconds later
.
V
client --- ask for next question / send answer ---> server (receive request at timestamp `B`)
|
+-----------------------------------------------------+
v
server logic:
duration = B - A
if(duration > 10 seconds) {
// allocated time exceeded
}
However, I see a few potentional flaws with this. The time it takes for the question to arrive to the client from the server and the time between when the server sent the questioon (time A) to the time that the client-side timer starts won't be instentationous and will depend on the ping / connection that the user has to the server. Similar ping issues exist when the client asks for the next question. Moreover, I'm worried that if the client-side timer which is supposed to run for 10 seconds lags behind a little, then it would also cause the server-side check to fail. As a result, checking if the duration exceeded 10 seconds isn't enough, and it would require some additional buffer. However, I feel like arbitarly hard-coding the buffer to something like 1 or 2 seconds could potentionally still lead to issues and feels like a bit of a hacky work-around that isn't very robust.
Question: I'm wondering if there is a different approach that I am missing to keep the client side timer unhackable and accurate. I also want to try and avoid creating seperate timers with setTimeout() or alike for each connected user on the server-side, as many users could be connected at one given point in time, and having so many timers queued up on the server feels unresourceful. I also want to try and keep the number of messages sent back and forth between the client and the server to a minimum.
What about a cookie?
Set a cookie with a unique token. Set its expiration to now()+15 seconds. Save the token and time on server side. Keep your client-side timer running with an auto submit after 10 seconds.
When the answer comes in, if there is no cookie... It certainly means the answer was sent after the delay (and the timer was hacked).
So a cookie expiration time of now() + 10 seconds + a grace period of ~5 additionnal seconds is supposed to be way enought to compensate the HTTP delays.
If they hack the timer, the cookie should have expired (and deleted). If they also hack the cookie expiration(!), anyway the token will be used to retreive the question sent datetime and you will compare it with the answer received datetime.
Instead of starting the clock on the server when the question is sent. You could start the clock on the server when the question is shows to the user (on the client).
Maintain 2 clocks one on the client other on the server.
Timestamp every time sensitive request (Start quiz timer and End quiz timer and check if the timestamp discrepancy is within acceptable tolerance.

Take away role after a given interval

setTimeout(() => {
targets.forEach(target => target.roles.remove(arole));
message.channel.send(`Removed ${arole}`);
}, RoleTime);
}
}
I use the above snippet to take away the roles of mentioned users after mentioned time. It works perfectly (I use ms to store RoleTime in milis). But when the time is like 12h the bot doesn't seem to reply and take away the role(Hosted on Heroku). Any idea why? What are the other ways to achieve this?
setTimeout doesn't work for long periods of time on services like heroku, and in general it's a bad idea to use setTimeout for operations like this anyway.
Some are saying to use a cron job for something like this, but due to the nature of the discord gateway, this is probably not a good idea.
Consider storing the expiry jobs in some sort of persistent database (sql, mongo etc)
Within your bot, every set period of time (30 seconds, 60 minutes) etc:
Query the database, fetch all expiry jobs that have passed or are equal to the expiry time
SELECT profile_snowflake,role_snowflake FROM expiry_jobs WHERE expiry_time <= ?
Iterate through each expiry job and remove the roles
Reschedule task
This allows for the bot to do expiry jobs across different processes - with services such as heroku, the bot can randomly restart which would completely reset the state of the bot, so you need to use some sort of persistent data store for the expiry jobs.

How often should I access my Heroku server to keep it awake? [duplicate]

This question already has answers here:
Easy way to prevent Heroku idling?
(24 answers)
Closed 3 years ago.
After some Chrome debugging I noticed it takes nearly 6 seconds to wake up which is not acceptable.
There are about 5 - 10 ways to keep it awake depending upon how you are counting: A google search pulls up a myriad of sites and ways to do this.
Some methods suggest regular pinging while others suggest regular GET requests.
I went with regular GET requests as it is just a small file to add to my server:
const http = require("http");
let INTERVAL = 300000; // 5 minute, keep a let for debugging
INTERVAL = 600000; // 10 minutes
const SITE = "http://www.your-site.ai";
let count = 0;
setInterval( () => {
count++;
wakeSite();
}, INTERVAL);
function wakeSite() {
const output = http.get(SITE);
console.log('DEBUG: ' + count);
}
function interface () {}
wakeSite();
module.exports = interface;
What should I set INTERVAL to and where is this documented in the Heroku documentation?
According to this Heroku article from 7 years ago, apps go to sleep after 1 hour.
Is this still valid and what wakes an app up? A simple ping request or does it need to be a full GET. Does Heroku support regular "josteling" of the app?
If an app has a free web dyno, and that dyno receives no web traffic in a 30-minute period, it will sleep.
https://devcenter.heroku.com/articles/free-dyno-hours
Connections to one-off dynos will be closed after one hour of inactivity (in both input and output). When the connection is closed, the dyno will be sent SIGHUP. This idle timeout helps prevent unintended charges from leaving interactive console sessions open and unused.
https://devcenter.heroku.com/articles/limits#dynos
So any web traffic will wake it up
There is already enough answer about how to prevent it,
like this one
After some Chrome debugging I noticed it takes nearly 6 seconds to wake up which is not acceptable.
If this is "not acceptable" you should pay for a hobby-tier (or higher) dyno. They never sleep, and carry additional benefits like ACM.
If you insist on staying on the free tier please bear in mind that there is a limit to the number of free dyno hours you can use:
Accounts are given a base of 550 free dyno hours each month. In addition to these base hours, accounts which verify with a credit card will receive an additional 450 hours added to the monthly free dyno quota. This means you can receive a total of 1000 free dyno hours per month, if you verify your account with a credit card.
450 hours is about 23 days (clearly not enough to run for a full month), and these hours are shared across all free dynos in your account. Even with a verified account you only get about 41 hours a month—not enough to run two dynos constantly.

How to get the latency time for a one way trip from client to server

I was wondering if there was a way to get the time (in ms) between the client sending a message to the server and the server receiving that message
I cannot compare the time in milliseconds with the server and client using Date.now() because every device might be off by a few seconds.
I can find the time for a two way trip, logging the time when I send a message and logging the time again when I receive a message in return from the server. However, The time it takes for a message to get from the client to the server may not be the same as it is for the message to get from the server to the client on a two way trip. So I cant just simply divide this time by 2.
Any suggestions on how I can find this time or at least the difference between Date.now() on the client and the server?
Thanks in advance.
You can achieve this if you first synchronize the clocks of both your server and client using NTP. This requires access to an external server, however you can configure NTP to be installed on your server as well (see ntpd)
There are several modules that implement NTP in node: node-ntp-client or sntp
Here's an example with node-ntp-client:
var ntpClient = require('ntp-client');
var clientOffset = 0;
ntpClient.getNetworkTime("pool.ntp.org", 123, function(err, date) {
if(err) {
console.error(err);
return;
}
clientOffset = Date.now() - date;
});
When sending data to the server, send the timestamp as well:
var clientTimestamp = Date.now() - clientOffset
Server would have its own offset. When receiving the package, it can calculate latency using:
var latency = Date.now() - serverOffset - clientTimestamp;
I was wondering if there was a way to get the time (in ms) between the client sending a message to the server and the server receiving that message
No, there is not. At least, not without a common clock reference.
If I were to mail you a letter, you know what day you received the letter on but you don't know when it was sent. Therefore, you have no idea how long it took the post office to route and deliver the letter to you.
One possible solution is for me to date the letter. When you receive it, you can compare the received date to the date I sent it and determine how many days it was in transit. However, what if I wrote down the wrong date? Suppose I thought it was Friday when it was really Wednesday. Then, you can't accurately determine when it was sent.
Changing this scale back to computers, we'll have to use our realtime clock (RTC) to timestamp the packet we send. Even with reasonable accuracy, our RTCs might be set a minute off from each other. I could send you a packet at 01:23:01.000Z my time, and you might receive it 10 milliseconds later... at 01:23:55.00Z your time and calculate that it took 54 seconds to reach you!
Even if you synchronize with NTP, over the internet, that's potentially 10s to 100s of milliseconds off.
The way very accurate clock synchronization is usually done is via GPS receivers, which by their nature serve as an extremely accurate clock source. If you and I were both very accurately sychronized to GPS receivers, I could send you a packet and you could calculate how long it took.
This is generally impractical, which is why when we ping stuff, we use round-trip time.

Google Pub/Sub How to set read timeout on Pull

I would like to set the read timeout of the pull request on a subscription. Right now the only options are to set returnImmediately=true or just wait until the pubsub returns, which seems to be 90 seconds if no messages is published.
I'm using the gcloud-node module to make calls to pubsub. It uses the request module under the hood to make the the gcloud api calls. I've updated my local copy of gcloud-node/lib/pubsub/subscription.js to set the request timeout to 30 seconds
this.request({
method: 'POST',
uri: ':pull',
timeout: 30000,
json: {
returnImmediately: !!options.returnImmediately,
maxMessages: options.maxResults
}
}
When I do this, the behavior I see is the connection will timeout on the client side after 30 seconds, but pubsub still has the request open. If I have two clients pulling on the subscription and one of them timeout after 30 seconds, then a message is published to the topic, it is a 50/50 chance that the remaining listening client will retrieve the message.
Is there a way to tell pubsub to timeout pull connections after a certain amount of time?
UPDATE:
I probably need to clarify my example a bit. I have two clients that connect at the same time and pull from the same subscription. The only difference between the two is that the first one is configured to timeout after 30 seconds. Since two clients are connected to the same subscription, pubsub will distribute the message load between the two of them. If I publish a message 45 seconds after both clients connect, there is a 50/50 chance that pubsub will deliver the message to the second client that has not timed out yet. If I send 10 messages instead of just one, the second client will receive a subset of the 10 messages. It looks like this is because my clients are in a long poll. If the client disconnects, the server has no idea and will try to send published messages on the response of the request that was made by the client that has timed out. From my tests, this is the behavior I've observed. What I would like to do is be able to send a timeout param in the pull request to tell subpub to send back a response after a 30000ms if no messages are published during that time. Reading over the API docs, this doesn't seem like an option.
Setting the request timeout as you have is the correct way to timeout the pull after 30 seconds. The existence of the canceled request might not be what is causing the other pull to not get the message immediately. If your second pull (that does not time out) manages to pull other messages that were published earlier, it won't necessarily wait for additional message that was published after the timeout to come in before completing. It only guarantees to not return more than maxMessages, not to return only once it has exactly maxMessages (if that many are available). Once your publish completes, some later pull will get the message, but there are no guarantees on exactly when that will occur.

Categories