We have a call to changes() running in a thread so we can monitor for changes to a table while doing other processing. When our process wants to close, the thread doesn't close and keeps the process running. Closing the connection with noreply_wait (see here) makes it happen because causes an error, so is there a better way to do this?
I would need a bit more info to be of assistance.
What type of environment are you connecting to the rethink server in?
What is launching the process that you need to monitor changes during and what type of events does that process emit upon completion?
Im going to assume your working in a node.js environment, unless told otherwise
const spawn = require('child_process').spawn;
r.table('yourTable').filter(function(row) {
return row('yourProperty').eq('trueVal');
})
.changes()
.run(rethinkConnection, function(err, cursor) {
if (err) {
console.log(err);
} else {
const doingThings = spawn('command', ['option1', 'option2']);
doingThings.on('exit', function() {
cursor.close();
});
cursor.each(function(err, row) {
if (err) {
console.log(err);
} else {
// do thing with changefeed row here
}
});
}
});
Related
The DOC says:
Note: If your add-in calls saveAsync on an item in compose mode in order to get an item ID to use with EWS or the REST API, be aware that when Outlook is in cached mode, it may take some time before the item is actually synced to the server. Until the item is synced, using the itemId will return an error.
As best I can tell, that's the cause of my ErrorItemNotFound problems trying to use that ID? (It's a shame Microsoft did not specifically tell us what error to expect).
Since my code is invoked asynchronously - how exactly do I wait for the noted "some time"? Do we set a timer to re-try every second or something? When do we give up?? Is there something else I can do which will give me a call-back to continue when the item sync has completed? [FYI - even waiting 10 seconds after the save does not work for me]
Be aware that I expect my users may be composing mail with large attachments, so while most no-attachment messages should sync in less than 1 second, folks attaching large pdf/zip/etc files could easily cause more than 1 minute delays here...
The best what you could do is to start polling for an item appeared on the server side. For example, you may try an ugly solution when you use sub-sequential EWS query with Id you've got from saveAsync in the loop and wait for success.
For example, I've noticed the following example how developers try to handle such scenarious:
app.makeEwsRequestAsync = function (request, callback, countRepeatIfCrash, callbackIfCrash) {
try {
Office.context.mailbox.makeEwsRequestAsync(request, function (asyncResult) {
try {
if (asyncResult.status !== 'succeeded') {
app.showError(asyncResult.error.message);
return;
} else {
var $result = app.getResponseElementByName(asyncResult.value, 'm:ResponseCode');
if ($result) {
var responseCOde = $result.text();
if (responseCOde !== 'NoError') {
if (countRepeatIfCrash > 0) {
setTimeout(function () {
app.makeEwsRequestAsync(request, callback, countRepeatIfCrash - 1);
}, 500);
} else if (callbackIfCrash) {
setTimeout(function() {
callbackIfCrash();
}, 500);
} else if (responseCOde === 'ErrorItemNotFound') {
app.showError('EWS ' + responseCOde, function () {
app.makeEwsRequestAsync(request, callback, 70);
});
}
else {
app.showError('EWS ' + responseCOde);
}
return;
}
}
}
callback(asyncResult);
} catch (e) {
app.showError(e);
}
});
} catch (e) {
app.showError(e);
}
}
See App for Outlook: EWS request failed with item Id returned by item.saveAsync on compose new message for more information.
You may also can try using the simple GetItem request:
<GetItem xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
</ItemShape>
<ItemIds><t:ItemId Id="' + itemId + '"/></ItemIds>
</GetItem>
The request should return ChangeKey if item was created on exchange.
MERN noob here. Trying to learn Async/Await by simulating a busy server where client browser only get the update > 3 seconds later (i will manually refresh localhost:3000, after 3 seconds. I only need help on Node.js/ server side for this question)
Could you help rectify codes below? Kindly avoid proposing other ways/methods but show me how to achieve using below example. Thanks in advance.
const app = require('express')()
async function getData() {
//Purpose: to simulate a busy server that returns data back to browser, after 3 seconds delay
await setTimeout(() => {
return 'After waiting 3 seconds, return this sentense as the required data to the browser.'
}, 3000);
}
app.get('/', async (req, res) => {
try {
const data = await getData()
await res.status(200).send(`${data}`)
} catch (err) {
await res.status(400).send(`Unable to get data. Error message, "${err}"`)
}
})
app.listen(3000)
The problem here is that setTimeout doesn't return a promise so you can't use await with it. It just executes the given function after 3 seconds. You can get what you want by wrapping it in a Promise like this:
const app = require('express')()
function getData() {
//Purpose: to simulate a busy server that returns data back to browser, after 3 seconds delay
return new Promise((resolve) => {
setTimeout(() => {
resolve('After waiting 3 seconds, return this sentense as the required data to the browser.');
}, 3000);
});
}
app.get('/', async (req, res) => {
try {
const data = await getData()
await res.status(200).send(`${data}`)
} catch (err) {
await res.status(400).send(`Unable to get data. Error message, "${err}"`)
}
})
app.listen(3008)
Note that you need a return statement to return the promise inside of getData. You didn't have a return statement originally which means the function returns undefined (or if marked as async it gives a Promise that resolves to undefined).
Here we don't need to use async/await because you're not needing to use await until in the app.get. Using async/await in getData could be added but it would be redundant.
Realize that aync/await uses Promises - it's just an easy way to work with Promises. So you can't await on anything but a Promise. Note that async really just means "this function returns a Promise and we'll wrap any result in a Promise if it isn't already a Promise". So you cannot use async/await without having a Promise at some point.
But if you really want to use async/await for some reason maybe this example would help you:
async function getData() {
//Purpose: to simulate a busy server that returns data back to browser, after 3 seconds delay
const result = await new Promise((resolve) => {
setTimeout(() => {
resolve('After waiting 3 seconds, return this sentense as the required data to the browser.');
}, 3000);
});
console.log('We are done waiting 3 seconds');
return result; // <-- this returns a Promise that resolves to the result string
}
just to be clear up-front, this questions is about .Net Core SignalR, not the previous version.
The new SignalR has an issue with WebSockets behind IIS (I can't get them to work on Chrome/Win7/IIS express). So instead I'm using Server Sent Events (SSE).
However, the problem is that those time out after about 2 minutes, the connection state goes from 2 to 3. Automatic reconnect has been removed (apparently it wasn't working really well anyway in previous versions).
I'd like to implement a heartbeat timer now to stop clients from timing out, a tick every 30 seconds may well do the job.
Update 10 November
I have now managed to implement the server side Heartbeat, essentially taken from Ricardo Peres' https://weblogs.asp.net/ricardoperes/signalr-in-asp-net-core
in startup.cs, add to public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
app.UseSignalR(routes =>
{
routes.MapHub<TheHubClass>("signalr");
});
TimerCallback SignalRHeartBeat = async (x) => {
await serviceProvider.GetService<IHubContext<TheHubClass>>().Clients.All.InvokeAsync("Heartbeat", DateTime.Now); };
var timer = new Timer(SignalRHeartBeat).Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(30));
HubClass
For the HubClass, I have added public async Task HeartBeat(DateTime now) => await Clients.All.InvokeAsync("Heartbeat", now);
Obviously, both the timer, the data being sent (I'm just sending a DateTime) and the client method name can be different.
Update .Net Core 2.1+
See the comment below; the timer callback should no longer be used. I've now implemented an IHostedService (or rather the abstract BackgroundService) to do that:
public class HeartBeat : BackgroundService
{
private readonly IHubContext<SignalRHub> _hubContext;
public HeartBeat(IHubContext<SignalRHub> hubContext)
{
_hubContext = hubContext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.SendAsync("Heartbeat", DateTime.Now, stoppingToken);
await Task.Delay(30000, stoppingToken);
}
}
}
In your startup class, wire it in after services.AddSignalR();:
services.AddHostedService<HeartBeat>();
Client
var connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
Remaining pieces of the initial question
What is left is how to properly reconnect the client, e.g. after IO was suspended (the browser's computer went to sleep, lost connection, changed Wifis or whatever)
I have implemented a client side Heartbeat that is working properly, at least until the connection breaks:
Hub Class: public async Task HeartBeatTock() => await Task.CompletedTask;
Client:
var heartBeatTockTimer;
function sendHeartBeatTock() {
connection.invoke("HeartBeatTock");
}
connection.start().then(args => {
heartBeatTockTimer = setInterval(sendHeartBeatTock, 10000);
});
After the browser suspends IO for example, the invoke method would throw an exception - which cannot be caught by a simple try/catch because it is async.
What I tried to do for my HeartBeatTock was something like (pseudo-code):
function sendHeartBeatTock
try connection.invoke("HeartbeatTock)
catch exception
try connection.stop()
catch exception (and ignore it)
finally
connection = new HubConnection().start()
repeat try connection.invoke("HeartbeatTock")
catch exception
log("restart did not work")
clearInterval(heartBeatTockTimer)
informUserToRefreshBrowser()
Now, this does not work for a few reasons. invoke throws the exception after the code block executes due to being run asynchronous. It looks as though it exposes a .catch() method, but I'm not sure how to implement my thoughts there properly.
The other reason is that starting a new connection would require me to re-implement all server calls like "connection.on("send"...) - which appears silly.
Any hints as to how to properly implement a reconnecting client would be much appreciated.
This is an issue when running SignalR Core behind IIS. IIS will close idle connections after 2 minutes. The long term plan is to add keep alive messages which, as a side effect, will prevent IIS from closing the connection. To work around the problem for now you can:
send periodically a message to the clients
change the idle-timeout setting in IIS as described here
restart the connection on the client side if it gets closed
use a different transport (e.g. long polling since you cannot use webSockets on Win7/Win2008 R2 behind IIS)
I've got a working solution now (tested in Chrome and FF so far). In the hope to either motivate you to come up with something better, or to save you a little while coming up with something like this yourselves, I'm posting my solution here:
The Heartbeat-"Tick" message (the server routinely pinging the clients) is described in the question above.
The client ("Tock" part) now has:
a function to register the connection, so that the callback methods (connection.on()) can be repeated; they'd be lost after just restarting a "new HubConnection" otherwise
a function to register the TockTimer
and a function to actually send Tock pings
The tock method catches errors upon sending, and tries to initiate a new connection. Since the timer keeps running, I'm registering a new connection and then simply sit back and wait for the next invocation.
Putting the client together:
// keeps the connection object
var connection = null;
// stores the ID from SetInterval
var heartBeatTockTimer = 0;
// how often should I "tock" the server
var heartBeatTockTimerSeconds = 10;
// how often should I retry after connection loss?
var maxRetryAttempt = 5;
// the retry should wait less long then the TockTimer, or calls may overlap
var retryWaitSeconds = heartBeatTockTimerSeconds / 2;
// how many retry attempts did we have?
var currentRetryAttempt = 0;
// helper function to wait a few seconds
$.wait = function(miliseconds) {
var defer = $.Deferred();
setTimeout(function() { defer.resolve(); }, miliseconds);
return defer;
};
// first routine start of the connection
registerSignalRConnection();
function registerSignalRConnection() {
++currentRetryAttempt;
if (currentRetryAttempt > maxRetryAttempt) {
console.log("Clearing registerHeartBeatTockTimer");
clearInterval(heartBeatTockTimer);
heartBeatTockTimer = 0;
throw "Retry attempts exceeded.";
}
if (connection !== null) {
console.log("registerSignalRConnection was not null", connection);
connection.stop().catch(err => console.log(err));
}
console.log("Creating new connection");
connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
connection.start().then(() => {
console.log("Connection started, starting timer.");
registerHeartBeatTockTimer();
}).catch(exception => {
console.log("Error connecting", exception, connection);
});
}
function registerHeartBeatTockTimer() {
// make sure we're registered only once
if (heartBeatTockTimer !== 0) return;
console.log("Registering registerHeartBeatTockTimer");
if (connection !== null)
heartBeatTockTimer = setInterval(sendHeartBeatTock, heartBeatTockTimerSeconds * 1000);
else
console.log("Connection didn't allow registry");
}
function sendHeartBeatTock() {
console.log("Standard attempt HeartBeatTock");
connection.invoke("HeartBeatTock").then(() => {
console.log("HeartbeatTock worked.") })
.catch(err => {
console.log("HeartbeatTock Standard Error", err);
$.wait(retryWaitSeconds * 1000).then(function() {
console.log("executing attempt #" + currentRetryAttempt.toString());
registerSignalRConnection();
});
console.log("Current retry attempt: ", currentRetryAttempt);
});
}
Client version based on ExternalUse's answer...
import * as signalR from '#aspnet/signalr'
import _ from 'lodash'
var connection = null;
var sendHandlers = [];
var addListener = f => sendHandlers.push(f);
function registerSignalRConnection() {
if (connection !== null) {
connection.stop().catch(err => console.log(err));
}
connection = new signalR.HubConnectionBuilder()
.withUrl('myHub')
.build();
connection.on("Heartbeat", serverTime =>
console.log("Server heartbeat: " + serverTime));
connection.on("Send", data =>
_.each(sendHandlers, value => value(data)));
connection.start()
.catch(exception =>
console.log("Error connecting", exception, connection));
}
registerSignalRConnection();
setInterval(() =>
connection.invoke("HeartBeatTock")
.then(() => console.log("Client heatbeat."))
.catch(err => {
registerSignalRConnection();
}), 10 * 1000);
export { addListener };
I am following this very clear tutorial.
All works except that when I get to the stage subscribing a user with pushManager when I call pushManager.subscribe() I don't get any response from the promise whose status is pending.
Maybe I am missing some glaring problem with my setup, which is as follows:
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is supported');
navigator.serviceWorker.register('service-worker.js')
.then(function(swReg) {
console.log('Service worker successfully registered.');
navigator.serviceWorker.ready.then(function() {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: ... // My application server key.
};
let promise = swReg.pushManager.subscribe(subscribeOptions)
console.log(promise) // Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
promise
.then(function(pushSubscription) {
console.log('Received PushSubscription: ', pushSubscription); // Never gets called.
})
.catch(function(err) {
console.error('Unable to subscribe.', err); // Never gets called.
});
})
})
.catch(function(err) {
console.error('Unable to register service worker.', err);
});
}
Should additional configuration be made somewhere else? Any advice is appreciated.
Just came across this thread.
The advice is to delete the GCM Store directory located in:
C:\Users\[username]\AppData\Local\Google\Chrome\User Data\Default\GCM Store
I first had to quit Chrome, delete the folder and then re-launch to get it to work.
Seems like a bug which is traceable back to June 2016 (Chrome 51) and even though it is now October 2017 (Chrome 61) it still seems to be an issue.
Hope this may help somebody.
I haven't received any event notifications and am wondering if I am missing something.
I followed the instructions from the Fabric Composer website to define the BasicEvent in my cto model and added the code for creating and emitting events in a transaction and updated the network. I created a separate eventListener.js program that subscribes to the events using the businessNetworkConnection using the code sample from the website.
After I start my eventListener.js app it seems to be listening (after receiving the connected status message in console, nothing else happens...it doesn't go back to the normal prompt line.)
I then execute the transaction that should emit the event and it runs successfully but no event is received in the other terminal window where the eventlistener.js is running.
Here is the key part of the eventListener.js program:
businessNetworkConnection.connect(connectionProfile, businessNetworkIdentifier, participantId, participantPwd)
.then((result) => {
businessNetworkDefinition = result;
console.log('Connected: BusinessNetworkDefinition obtained=' + businessNetworkDefinition.getIdentifier());
});
businessNetworkConnection.on('event', (event) => {
// event: { "$class": "org.namespace.BasicEvent", "eventId": "0000-0000-0000-000000#0" }
console.log(event);
});
Is the businessNetworkConnection.on('event', (event) ... command supposed to cause the program to appear to hang while its listening?
If so, is there something else that could be done to troubleshoot where the problem is?
I'm using v0.6 HLF on local Docker.
I think your issue is that you don't wait for the businessNetworkDefinition to be connected before you register your listener. Remember that the then block is executed asynchronously and your following businessNetworkConnection.on code will execute immediately.
You should add a second then block to register your listener after the businessNetworkDefinition has been connected.
e.g.
return adminConnection.connect('hlfv1', 'admin', 'adminpw')
.then(() => {
return BusinessNetworkDefinition.fromDirectory(path.resolve(__dirname, '..'));
})
.then((businessNetworkDefinition) => {
return adminConnection.deploy(businessNetworkDefinition);
// return true;
})
.then(() => {
businessNetworkConnection = new BusinessNetworkConnection();
return businessNetworkConnection.connect('hlfv1', 'my-network', 'admin', 'adminpw');
})
.then(() => {
businessNetworkConnection.on('event', (event) => {
console.log( '****** received the event ' + JSON.stringify(businessNetworkConnection.getBusinessNetwork().getSerializer().toJSON(event)));
});
});
I wrote a sample code for a basic sample application developed by Hyperledger team. You can check out about from this code snippet: SampleEventListener