How can I avoid "Callback was already called" with async.queue? - asynchronous

I'm trying to make a series of calls using async.queue, and each call has its own callback where I'm using mocha to test that an expected result was returned.
When I use a concurrency value of 1 (my NUMBER_OF_THREADS variable), this all works great. However, when I use any value greater than 1, I get errors stating, "Error: Callback was already called." For instance, If I send 10 messages, and set my NUMBER_OF_THREADS to 5, the first 5 messages will go smoothly, but then I start seeing the duplicate-callback errors around message 6 or 7 (see below). Do you know how I can avoid this error?
My test file (where async queue is defined):
var myQueue = async.queue(function(options, callback){
var counter = options.counter;
myService.sendMyMessage(options.text, counter, function(result) {
var myText = result.content.text;
console.log("Response " + myService.counter + ": " + myText);
responses.push(myText);
callback();
});
}, NUMBER_OF_THREADS);
myQueue.drain = function(){
console.log("sends completed");
for (var i = 0; i < numberOfSends; i++) {
assert.equal(myExpectedResponse,responses[i],"text doesn't match");
}
done();
};
for (var j = 1; j <= numberOfSends; j++) {
var options = {
counter: j,
text: "Hello_" + j
};
myQueue.push(options);
}
My service file (where the sends and responses happen):
myService.callback = function() {};
myService.sendMyMessage = function(message, counter, myCallback) {
console.log("Sending message " + counter + ": " + message);
var myContent = JSON.stringify(myModel.makeMessage(message));
myModel.post(content)
.then(function(res) {
myService.callback = myCallback;
});
};
myService.otherService = function(done) {
app = express();
app.use(express.bodyParser());
app.post('/myRoute/events', function(req, res, next) {
var response = {
"myId": "1234567890",
"myVersion": 1
};
res.set('Content-Type', 'application/json;charset=UTF-8');
res.send(JSON.stringify(response));
if (myService.callback)
{
myService.counter ++;
myService.callback(req.body);
//myService.callback = null;
}
else
{
console.log('the callback is NULL');
}
});
My results in the console:
Sending message 1: Hello_1
Sending message 2: Hello_2
Sending message 3: Hello_3
Sending message 4: Hello_4
Sending message 5: Hello_5
Response 1: myResponse
Sending message 6: Hello_6
Response 2: myResponse
Sending message 7: Hello_7
Response 3: myResponse
Sending message 8: Hello_8
Response 4: myResponse
Sending message 9: Hello_9
Response 5: myResponse
Sending message 10: Hello_10
Response 6: myResponse
Response 7: myResponse
Error: Callback was already called.
at myFile.js:12:34
If I un-comment the myService.callback = null line, the the first send of my last batch causes myService.callback to be null too early. For example, if I send 10 requests with NUMBER_OF_THREADS=5, requests 1 thru 5 will work great. However, once I send requests 1 thru 10, request #10 will nullify myService.callback too early. Example responses:
Sending message 1: Hello_1
Sending message 2: Hello_2
Sending message 3: Hello_3
Sending message 4: Hello_4
Sending message 5: Hello_5
Response 1: myResponse
Sending message 6: Hello_6
Response 2: myResponse
Sending message 7: Hello_7
Response 3: myResponse
Sending message 8: Hello_8
Response 4: myResponse
Sending message 9: Hello_9
Response 5: myResponse
Sending message 10: Hello_10
Response 6: myResponse
the callback is NULL
the callback is NULL
the callback is NULL
the callback is NULL

I've fixed this now.
In my test file, I'm simply calling sendMyMessage now; I no longer expect a callback from sendMyMessage. In drain, I wait for the total number of responses to be hit, and then loop through those responses.
var myQueue = async.queue(function(options, callback){
var counter = options.counter;
myService.sendMyMessage(options.text, counter);
callback();
}, NUMBER_OF_THREADS);
myQueue.drain = function(){
var myInterval = setInterval(function() {
if (myService.responseCounter == myNumberOfMessages) {
clearInterval(myInterval);
for (var i = 0; i < myNumberOfMessages; i++) {
assert.equal(myExpectedResponse,myService.responses[i],"error");
}
done();
}
}, 5000);
};
for (var j = 1; j <= myNumberOfMessages; j++) {
var options = {
counter: j,
text: "Hello"
};
myQueue.push(options);
}
Then, in my service file, I'm now using an array of callbacks; I'm no longer dependent on a single callback getting set or nullified. Each call to myModel.post defines a new element in this callbacks array.
myService.sendLineMessage = function(message, counter) {
myModel.post(content, sign, config, request, log, run)
.then(function(res) {
myService.callbacks[counter] = function(result) {
var myText = result.content.text;
myService.responses.push(resultText);
};
});
};
myService.otherService = function(done) {
app = express();
app.use(express.bodyParser());
app.post('/myRoute/events', function(req, res, next) {
var response = {
"myId": "1234567890",
"myVersion": 1
};
res.set('Content-Type', 'application/json;charset=UTF-8');
res.send(JSON.stringify(response));
myService.responseCounter++;
if (myService.callbacks[myService.responseCounter])
{
myService.callbacks[myService.responseCounter](req.body);
myService.callbacks[myService.responseCounter] = null;
}
else
{
console.log('the callback is NULL');
}
});

Related

ClientException, and i can't print the returned value (the request body)

Alright i'm losing my mind here,
in my flutter app, i'm using this function to perform post requests :
Future<Map> postRequest(String serviceName, Map<String, dynamic> data) async {
var responseBody = json.decode('{"data": "", "status": "NOK"}');
try {
http.Response response = await http.post(
_urlBase + '$_serverApi$serviceName',
body: jsonEncode(data),
);
if (response.statusCode == 200) {
responseBody = jsonDecode(response.body);
//
// If we receive a new token, let's save it
//
if (responseBody["status"] == "TOKEN") {
await _setMobileToken(responseBody["data"]);
// TODO: rerun the Post request
}
}
} catch (e) {
// An error was received
throw new Exception("POST ERROR");
}
return responseBody;
}
The problems are :
I get a ClientException (Not every time)
In another class, I stored the result of this function in a variable, it's supposed to return a Future<Map<dynamic, dynamic>>, when i printed it it shows :
I/flutter ( 9001): Instance of 'Future<Map<dynamic, dynamic>>'
But when i run the same post request directly (without using a function) it worked, and it shows the message that i was waiting for.
note: in both cases (function or not), in the server side it was the same thing.
this is the function where i used the post request:
void _confirm() {
if (_formKey.currentState.saveAndValidate()) {
print(_formKey.currentState.value);
var v = auth.postRequest("se_connecter", _formKey.currentState.value);
print(v);
} else {
print(_formKey.currentState.value);
print("validation failed");
}
}
Well for the second problem, i just did these changes:
void _confirm() async {
and
var v = await auth.postRequest('se_connecter', _formKey.currentState.value);
and yes it is stupid.
For the exception, it was the ssl encryption that caused it, so i removed it from my backend.

Error while making request: socket hang up. Error code: ECONNRESET

I'm using node.js as a backend server for sending push notification from the Firebase Cloud Messaging service. The notifications are working fine with local server but on live server, I get this error:
Error while making request: socket hang up. Error code: ECONNRESET
Things to consider are that...
Number of users are in the thousands on live server
Firebase version is firebase-admin#6.5.1
Previously unregistered tokens are still there. But now registered tokens are being stored.
This is my code for sending notifications:
for (let c = 0; c < tokens.length; c++)
{
let notifyTo = tokens[c];
const platform = platforms[c];
let payload;
if (platform === "ios") {
payload = {
notification: {
title: "title",
subtitle :"messgae",
sound: "default",
badge: "1"
},
data: {
sendFrom: "",
notificationType: "",
flag: "true"
}
};
} else if (platform === "android") {
payload = {
data: {
title: "",
message : "",
flag: "true"
}
};
}
const registrationtoken = notifyTo;
await admin.messaging().sendToDevice(registrationtoken, payload)
.then(function (response) {
console.log("Successfully sent message:");
})
.catch(function (error) {
console.log("Error sending message: ");
});
}
Your issue is caused by your function taking too long to respond to the client (more than 60 seconds) and is caused by the following line:
await admin.messaging().sendToDevice(registrationtoken, payload)
Because you are waiting for each call of sendToDevice() individually, you are running your for-loop in synchronous sequential order, rather than asynchronously in parallel.
To avoid this, you want to make use of array mapping and Promise.all() which will allow you to build a queue of sendToDevice() requests. As in your current code, any failed messages will be silently ignored, but we will also count them.
Your current code makes use of two arrays, tokens and platforms, so in the code below I use a callback for Array.prototype.map() that takes two arguments - the current mapped value (from tokens) and it's index (your for-loop's c value). The index is then used to get the correct platform entry.
let fcmPromisesArray = tokens.map((token, idx) => {
let platform = platforms[idx];
if (platform === "ios") {
payload = {
notification: {
title: "title",
subtitle :"messgae",
sound: "default",
badge: "1"
},
data: {
sendFrom: "",
notificationType: "",
flag: "true"
}
};
} else if (platform === "android") {
payload = {
data: {
title: "",
message : "",
flag: "true"
}
};
}
return admin.messaging().sendToDevice(token, payload) // note: 'await' was changed to 'return' here
.then(function (response) {
return true; // success
})
.catch(function (error) {
console.log("Error sending message to ", token);
return false; // failed
});
});
let results = await Promise.all(fcmPromisesArray); // wait here for all sendToDevice() requests to finish or fail
let successCount = results.reduce((acc, v) => v ? acc + 1 : acc, 0); // this minified line just counts the number of successful results
console.log(`Successfully sent messages to ${successCount}/${results.length} devices.`);
After this snippet has run, don't forget to send a result back to the client using res.send(...) or similar.

Meteor DDP Call returns undefined when too long

I got 2 servers, one for the main app and another one for huge tasks.
User -> Server 1 -> Server 2
Server 1: Main app & Easy tasks
Server 2: Huge Tasks
When I call a server 2's function which takes a long time to answer, server 1 receive undefined when server 2 answer a good result. However, if server2's function takes less than 1 minute to answer, server 1 got the result sent by server 2 and then send it back to the client.
Why it doesn't work only for functions which take more than 1 minute to compute ?
Client :
Meteor.call('reporting.default', params.subReport, params, function(error, result) {
if (result) self.setState({data: result});
else self.setState({data: error.message});
});
Server 1:
Meteor.methods({
'reporting.default'(subReport, params) {
this.unblock();
return Meteor.callWorker('reporting.' + subReport, Meteor.callId(), params).then((result, error) => { if (error) return error; else return result; }).await();
},
});
Meteor.worker = DDP.connect('localhost:' + Meteor.settings.heavyTasksServer.port);
Meteor.callWorker = (method, ...myParameters) => new Promise((resolve, reject) => {
console.log(method + ": REQUEST");
Meteor.worker.call(method, ...myParameters, (err, res) => {
if (err) {
console.log(method + ": ERROR");
reject(err);
}
else {
console.log(method + ": ANSWER");
resolve(res);
}
});
});
Meteor.callId = function () {
const d =new Date();
return d.getUTCFullYear() +""+ (d.getUTCMonth()+1) +""+ d.getUTCDate() +""+ d.getUTCHours() +""+ d.getUTCMinutes() +""+ d.getUTCSeconds() +""+ d.getUTCMilliseconds() + "-" + Meteor.userId();
};
Server 2:
Meteor.methods({
'reporting.clientsAssets'(callId, params) {
this.unblock();
const funcName = "reporting.clientsAssets";
if (canRunQuery(1, callId, arguments, funcName)) {
console.log(funcName + ": START");
const data = reportingClientsAssets(params);
console.log(funcName + ": END");
terminateQuery(callId);
return data;
}
}
});
You could consider an asynchronous model instead of a synchronous one (which is probably timing out).
Let's think about a queuing mechanism... create a collection call jobs (or whatever you prefer), and server 1 creates a record in the job collection with a status of 'ready'.
A timed task (you can use node-cron for this) runs on server 2 say every minute, and looks for jobs with a status of 'ready'. It takes the first one, sets the status to 'running' and then calls the function to do the work.
When that function completes, it sets the status of the task to 'complete'.
You make use of Meteor's reactivity, so that the user can see the status of the job, once it is started, it moves to 'running', and then to 'complete' once it is done. At that point a link may appear so they have access to the data, report or whatever is produced.
No timeout issues with this mechanism, and it's nicely decoupled.

Google Speech API streaming audio from websocket

I am trying to get a final speech transcription/recognition result from a Fleck websocket audio stream. The method OnOpen executes code when the websocket connection is first established and the OnBinary method executes code whenever binary data is received from the client. I have tested the websocket by echoing the voice into the websocket and writing the same binary data back into the websocket at the same rate. This test worked so I know that the binary data is being sent correctly (640 byte messages with a 20ms frame size).
Therefore, my code is failing and not the service. My aim is to do the following:
When the websocket connection is created, send the initial audio config request to the API with SingleUtterance == true
Run a background task that listens for the streaming results waiting for isFinal == true
Send each binary message received to the API for transcription
When background task recognises isFinal == true, stop current streaming request and create a new request - repeating steps 1 through 4
The context of this project is transcribing all single utterances in a live phone call.
socket.OnOpen = () =>
{
firstMessage = true;
};
socket.OnBinary = async binary =>
{
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
if (firstMessage == true)
{
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding = RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 16000,
LanguageCode = "en",
},
SingleUtterance = true,
}
});
Task getUtterance = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(
default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream.Current.Results)
{
if (result.IsFinal == true)
{
Console.WriteLine("This test finally worked");
}
}
}
});
firstMessage = false;
}
else if (firstMessage == false)
{
streamingCall.WriteAsync(new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString.CopyFrom(binary, 0, 640)
}).Wait();
}
};
.Wait() is a blocking call being called in an async/await. They don't mix well and can lead to deadlocks.
Simply keep the code async all the way through
//...omitted for brevity
else if (firstMessage == false) {
await streamingCall.WriteAsync(new StreamingRecognizeRequest() {
AudioContent = Google.Protobuf.ByteString.CopyFrom(binary, 0, 640)
});
}

Get request async call in protractor

I have a test case where my code make request to server and check if the filename with specified date exists. Since GET request is async call, how can I make sure that I have the filename from the server before I check if it is a specified date?
Here's excerpt of my code :
var re = new RInterface();
it('data show exists', function() {
target.each(function(ele){
browser.actions().mouseMove(ele).perform();
re.get(function(result){
expect(result).toEqual(true);
});
});
});
RInterface.js
var Service = function() {
var serv = this;
var uname = atob(settings.username);
var pwd = atob(settings.password);
var url = 'https://' + uname + ':' + pwd + '#' + settings.Url + '/' + settings.format + '/' + settings.period;
var completeURL = url;
var today = DateString();
serv.get = function(callback) {
var dataStrAry = [];
var count = 0;
request(completeURL, function (error, response, body) {
if (!error && response.statusCode == 200) {
var serverData = JSON.parse(body);
var split = serverData[serverData.length-1].Name.split(" ");
var target = split[split.length-1].split(".")[0];
// Check if the file with current date is available
// If it is, then assume data is saved on the server
if(target == today) {
console.log("equal");
callback(true);
}
else {
console.log("not equal");
callback(false);
}
}
else {
console.log("errror call");
callback(false);
return;
}
});
};
So, re.get is where I make GET request to the server and I passed callback function to be called at the end of get request. The problem, I think is protractor complete executing the test before my code gets data from the server. How do I force protractor to wait so that I can check the returned data? My current workaround is put the get request inside beforeEach and seems that protractor forces test to wait for it finish executing.
You need to handle the result of your request with a Promise if you want the control flow to wait for it:
var re = new RInterface();
it('data show exists', function() {
target.each(function(ele){
browser.actions().mouseMove(ele).perform();
expect(re.get()).toEqual(true);
});
});
var Service = function() {
...
this.get = function() {
var defer = protractor.promise.defer();
...
request(completeURL, function (error, response, body) {
if (!error && response.statusCode == 200) {
var result = ...
defer.fulfill(result);
} else {
defer.reject(error);
}
});
return defer.promise;
};
};
You can make it easier by using browser.wait, just wrap the request function (which returns a promise) like
browser.wait(request...).then(result => {
// here continue execution with the result of the request
}
This will make the browser wait for your promise.

Resources