Vercel serverless function timeout, using functions that take more then 10 sec to execute - aws-lambda

I am building a pet project like a multiplayer quiz using Next JS deployed on Vercel.
Everything works perfect on a localhost, but when I deploy it on Vercel as a cloud function (in the API route) I meet a problem that serverless function can only last for 10 seconds.
So I want to understand what is the best practice to handle the problem.
version of the cycle in an api route looks like this:
export async function quizGameProcess(
roomInitData: InitGameData,
questions: QuestionInDB\[\],
) {
let questionNumber = 0;
let maxPlayerPoints = 0;
const pointsToWin = 10;
while (maxPlayerPoints \< pointsToWin) {
const currentQuestion = questions\[questionNumber\];
// Wait 5 seconds before start
await new Promise(resolve =\>
setTimeout(resolve, 5000),
);
// Show question to players for 15 seconds
await questionShowInRoom(roomInitData, currentQuestion);
await new Promise(resolve => setTimeout(resolve, 15000)); <====== Everything works great until this moment
// Show the right answer for 5 seconds
await AnswerPushInRoom(roomInitData);
await new Promise(resolve =>
setTimeout(resolve, 5),
);
maxPlayerPoints = await countPlayerPoints(roomInitData)
...
questionNumber++
So i need 15 seconds to show players the question and cloud function returns error while invoking it.
questionShowInRoom() function just changes a string in the database from :
room = {activeWindow: prepareToStart}
to
room = {activeWindow: question}
after 15 seconds it must change it to:
room = {activeWindow: showAnswer}
So the function must return something before 10 seconds, but if you return something - the route stops execution.
I cant use VPS because the project must stay as one Next JS project folder and must be easy maintained in one place and be free.
So if i divide the code - and make some 'worker', how it should be invoked? By some other route? isnt that a bad practice?
Or of it will be the frontend just making polling every second trying to invoke it until timestamp difference become more than 15 seconds.. looks like a strange decision.
So what is the best practice to handle the problem?

Related

Hyperledger composer performance(adding asset) is very low

Following code is simple code to check how many entities can be added per second or minute.
createAsset is calling backend(http:localhost:3000) and add data using post.
When I did test using this code, it took 23 seconds to add 10 entities.
I am using composer 0.19.12 and fabric 1.1. When I checked some thread from GitHub, performance has improved using indexing couchdb. How can I use that feature? (I need to check again, but it seems that it is default feature of recent composer version)
addEntities: async function() {
var start = 0;
var end = start + 100;
var sd = new Date();
console.log(sd.getHours()+':'+sd.getMinutes()+':'+sd.getSeconds()+'.'+sd.getMilliseconds());
for(var i = start; i<end; i++) {
entityData.id = i.toString();
await this.createAsset('/Entity', 'model.Entity', entityData);
}
var ed = new Date();
var totalTime = new Date(ed.getTime()-sd.getTime());
console.log(totalTime.getMinutes()+':'+totalTime.getSeconds()+'.'+totalTime.getMilliseconds());
},
My model is really simple as follows.
asset Entity identified by id {
o String id
}
I have changed the test code to send multiple transactions as follows following david_k's advice.
addEntities: async function() {
var start = 15000;
var dataNumber = 1200;
var loopNumber = 400;
var end = start + dataNumber;
var sd = new Date();
console.log(sd.getHours()+':'+sd.getMinutes()+':'+sd.getSeconds()+'.'+sd.getMilliseconds());
var tasks = [];
for(var i = start; i<end; i++) {
entityData.id = i.toString();
if((i-start)%loopNumber === loopNumber - 1) {
await this.createAsset('/Entity', 'model.Entity', entityData);
console.log('--- i: ' + i + ' loops completed');
}
else {
this.createAsset('/Entity', 'model.Entity', entityData);
}
}
var ed = new Date();
var totalTime = new Date(ed.getTime()-sd.getTime());
console.log(totalTime.getMinutes()+':'+totalTime.getSeconds()+'.'+totalTime.getMilliseconds());
},
The purpose of change is send multiple requests at the same time, and it seems work well because it shows much better performance compared to previous code. However, the performance is still around 8 TPS. As original test code was 1 transaction per 2sec~3sec, it improved a lot. But, 8TPS looks that it cannot be used for commercial application at all. Even it is not good for test purpose as well. Could someone give some advice for this?
That sounds about right looking at your example code and I am assuming you are using either the fabric-dev-servers package which is a very simple fabric network to help get users started with developing a business network and want to try out on a hyperledger fabric network, or you are using the byfn network from the multi-org tutorial which is a hyperledger fabric example of a 2 organisation network in a consortium to demonstrate the required operational steps of composer in a multi-org fabric setup.
Hyperledger Fabric is a distributed ledger technology based around eventual consistency. Composer implements a submit/notify model such that once a transaction has been submitted it will notify the client when that transaction has been committed to the ledger. You can configure which Peers in a network you are interested in informing you when that occurs, but the default is all of them and so the rest server responds once all peers have committed it to the ledger.
Hyperledger fabric doesn't commit individual transactions, it batches them up into blocks and these blocks get committed to the ledger, and it will wait a period of time before building that block with the current set of transactions that have been submitted for ordering, so blocks can contain one or more transactions. You need to configure fabric for your use case to determine how transactions are batched into blocks.

Throttle service with multiple, independant callers

I want to ensure that a given API call is throttled so that for a given time interval, only a single request is fired, and that the other, throttled requests wait and receive the results of the request that was actively fired
Example
const generateReport = (args) => client.get(...)
const generateReportA = (argsForA) =>
generateReport(argsForA).then(saveReportSomewhere)
const generateReportB = (argsForB) =>
generateReport(argsForB).then(saveReportSomewhere)
const generateReportC = (argsForC) =>
generateReport(argsForC).then(saveReportSomewhere)
If we then run the statements below
generateReportA(...).then(console.log) // should log result of C
generateReportB(...).then(console.log) // should log result of C
generateReportC(...).then(console.log) // should log result
right after each other, I only want to fire the request associated with generateReportC and I'd like both generateReportA and generateReportB to receive and handle the result of generateReportC.
In the end generateReport should have been called once and saveReportSomewhere should have been called 3 times, each with the result from generateReportC
Is this possible?
This will fire C request get result and save it, then trigger A and B at the same time with result from C and save results immediately after every emit.
generateReportC.pipe(
tap(cResult => saveReportSomewhere(cResult)),
mergeMap(cResult => merge(generateReportA(cResult), generateReportB(cResult))),
tap(result => saveReportSomewhere(result))
).subscribe();

Do side effect if observable has not emitted a value within X amount of time

I'm working on a use case that requires that if an observable has not emitted a value within a certain amount of time then we should do some side effect.
To give a practical use case:
open web socket connection
if no message has been sent/received within X time then close web socket connection and notify user
This requires for a timer to be initiated on every emitted value and upon initial subscription of observable which will then run some function after the allotted time or until a value is emitted in which the timer resets. I'm struggling to do this the Rx way. Any help would be appreciated :)
debounceTime is the operator you're looking for: it only emits a value if no others follow within a specific timeout. Listening for the first message of the debounced stream will let you time out and clean up your websocket connection. If you need to time out starting from the opening of the stream, you can simply startWith. Concretely:
messages$.startWith(null)
.debounceTime(timeout)
.take(1)
.subscribe(() => { /* side effects */ });
Edit: if instead you're looking to end the a message stream entirely when it times out (e.g. you clean up in the onComplete handler), just cram debounceTime into a takeUntil:
messages$.takeUntil(
messages$.startWith(null)
.debounceTime(timeout)
).subscribe(timeout_observer);
With a timeout_observable: Observer<TMessage> that contains your cleanup onComplete.
You can do this with race:
timer(5000).race(someSource$)
.subscribe(notifyUser);
If someSource$ notifies faster than timer(5000) (5 seconds), then someSource$ "wins" and lives on.
If you only want one value from someSource$, you can obviously have a take(1) or first() on someSource$ and that will solve that issue.
I hope that helps.
Might not be the perfect answer but it does what you asked, it depends on how you want to disconnect, there might be some variation to be done
const source = new Rx.Subject();
const duration = 2000;
source.switchMap(value=>{
return Rx.Observable.of(value).combineLatest(Rx.Observable.timer(2000).mapTo('disconnect').startWith('connected'))
}).flatMap(([emit,timer])=>{
if(timer=='disconnect'){
console.log('go disconnect')
return Rx.Observable.throw('disconnected')
}
return Rx.Observable.of(emit)
})
//.catch(e=>Rx.Observable.of('disconnect catch'))
.subscribe(value=>console.log('subscribed->',value),console.log)
setTimeout(() => source.next('normal'), 300);
setTimeout(() => source.next('normal'), 300);
setTimeout(() => source.next('last'), 1800);
setTimeout(() => source.next('ignored'), 4000);
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>
A timer is initiated on each element and if it takes 4 seconds to be shown, then it will timeout and you can execute your function in the catchError
Here an example, it displays aa at T0s, then bb at t3s, then timeout after 4 second because the last one cc takes 10s to be displayed
import './style.css';
screenLog.init()
import { from } from 'rxjs/observable/from';
import { of } from 'rxjs/observable/of';
import { race } from 'rxjs/observable/race';
import { timer } from 'rxjs/observable/timer';
import { groupBy, mergeMap, toArray, map, reduce, concatMap, delay, concat, timeout, catchError, take } from 'rxjs/operators';
// simulate a element that appear at t0, then at t30s, then at t10s
const obs1$ = of('aa ');
const obs2$ = of('bb ').pipe(delay(3000));
const obs3$ = of('cc ').pipe(delay(10000));
const example2 = obs1$.pipe(concat(obs2$.pipe(concat(obs3$))), timeout(4000), catchError(a => of('timeout'))); // here in the catchError, execute your function
const subscribe = example2.subscribe(val => console.log(val + ' ' + new Date().toLocaleTimeString()));

Sending events between two Meteor servers

Is there a way to send events between two Meteor servers? I know I can connect Server1 to Server2 (and vice versa) using DDP.connect and just call methods between the two servers. This will not work for me, because one of my servers (Server1) will be at my house behind a dynamic IP and firewall. DDP.connect requires a url. What is the best way, if any, to communicate between the two servers? I am thinking the only way to do this would be something like Socket.io where Server1 connects to Server2. I'm not sure if this can be done in Meteor though. Thanks.
You can do this by using DDP.connect to connect server 1 to server 2 on startup, then subscribing to a collection that server 2 publishes, for example:
On Server 2 (known URL):
var Events = new Meteor.Collection("events");
Meteor.publish("events", function () {
return Events.find({});
}
On Server 1 (at your house):
var EventConnection = DDP.connect("<server 2 URL>");
var Events = new Meteor.Collection("events", {connection: EventConnection});
EventConnection.subscribe("events");
Events.find({}).observe({
added: function (newEvent) {
// do something with newEvent
}
});
Then, whenever server 2 adds an object to the Events collection, you will get it on server 1 via the connection. Watch out, though - every time server 1 connects to server 2 it will get all previous events as well. If you don't want that to happen, you need to use the ready callback on subscribe:
Revised code for Server 1:
var EventConnection = DDP.connect("<server 2 URL>");
var Events = new Meteor.Collection("events", {connection: EventConnection});
EventConnection.subscribe("events", function () {
Events.find({}).observe({
added: function (newEvent) {
// do something with newEvent
}
});
});

Is measuring js execution time a way to tell how quickly the app is responding to requests?

I have something like a microtime() function at the very start of my node.js / express app.
function microtime (get_as_float) {
// Returns either a string or a float containing the current time in seconds and microseconds
//
// version: 1109.2015
// discuss at: http://phpjs.org/functions/microtime
// + original by: Paulo Freitas
// * example 1: timeStamp = microtime(true);
// * results 1: timeStamp > 1000000000 && timeStamp < 2000000000
var now = new Date().getTime() / 1000;
var s = parseInt(now, 10);
return (get_as_float) ? now : (Math.round((now - s) * 1000) / 1000) + ' ' + s;
}
The code of the actual app looks something like this:
application.post('/', function(request, response) {
t1 = microtime(true);
//code
//code
response.send(something);
console.log("Time elapsed: " + (microtime(true) - t1));
}
Time elapsed: 0.00599980354309082
My question is, does this mean that from the time a POST request hits the server to the time a response is sent out is give or take ~0.005s?
I've measured it client-side but my internet is pretty slow so I think there's some lag that has nothing to do with the application itself. What's a quick and easy way to check how quickly the requests are being processed?
Shameless plug here. I've written an agent that tracks the time usage for every Express request.
http://blog.notifymode.com/blog/2012/07/17/profiling-express-web-framwork-with-notifymode/
In fact when I first started writing the agent, I took the same approach. But I soon realized that it is not accurate. My implementation tracks the time difference between request and the response by substituting the Express router. That allowed me to add tracker functions. Feel free to give it a try.

Resources