Retrieval of /signalr/hubs gets very slow after 5-10 minutes, I've to restart app pool again and again. Is there any way to cache this?
What I've done-
Checked all the memory and cpu allocations for App pool but couldn't
find anything.
Searched on google but didn't find anything relevant
You can try putting this jquery functions in you View Page.
var tryingToReconnect = false;
$.connection.hub.reconnecting(function() {
tryingToReconnect = true;
});
$.connection.hub.reconnected(function() {
tryingToReconnect = false;
});
$.connection.hub.disconnected(function() {
if(tryingToReconnect) {
notifyUserOfDisconnect(); // Your function to notify user.
}
});
Also check if you network connection is slow.
$.connection.hub.connectionSlow(function() {
notifyUserOfConnectionProblem(); // Your function to notify user.
});
You'll get exact idea about the problem weather issue is because of signalR or not.
Hope this helps.
Related
I'm successfully using Azure Mobile Services and Xamarin Forms to perform CRUD operations on an SQL DB hosted with Azure. The offline sync portion stores the data in an SQLite db on the phone. There's been a few bumps along the way to get it working as smoothly as we have it now, but this remains to be the last hurdle.
Problem
When the device has no connection (tested using Airplane mode on a variety of physical and emulated devices) - the first time it goes to access any of the offline data, it takes a very long time to return anything. This is the case if the data exists in the SQLite DB or not.
There is no exception thrown, or anything that I can see printed to the logs that indicates what the delay might be.
To give an idea, a PullAsync() on 20 rows might take 5 seconds while online, and that data is stored to the SQLite DB. After putting the device into offline mode, that same operation may take up to 60 seconds. These numbers are quite arbitrary, but the delay is noticeably much too long.
To add to this, this long load only occurs the very first time any Offline Sync method is called. After that, every method is near instant, as I would expect it to be - but why not the first time?
Expected Result
I would expect that because the data is stored on the device already, and no internet connection can be detected, it should return the data almost instantly.
Code
Sync Class
The GetPolicies() method is where the delay would occur.
This is a sample of one of the components. Every other component is the same format, but different data.
IMobileServiceSyncTable<policy_procedure> policyTable = SyncController.policyTable;
public async Task<List<policy_procedure>> GetPolicies(string companyId)
{
//SemaphoreSlim
await SyncController.dbOperation.WaitAsync();
try
{
await SyncController.Initialize();
await policyTable.PullAsync("policy_procedure", policyTable.Where(p => p.fk_company_id == companyId).Where(p=> p.signature!=null || p.signature!=""));
return await policyTable.ToListAsync();
}
catch (Exception ex)
{
//For some reason, when this method is called and the device is offline, it will fall into this catch block.
//I assume this is standard for offline sync, as it's trying to do a pull with no connection, causing it to fail.
//Through using breakpoints, the delay occurs even before it reaches this catch statement.
Console.WriteLine(ex);
return await policyTable.ToListAsync();
}
finally
{
SyncController.dbOperation.Release();
}
}
Sync Controller
public static SemaphoreSlim dbOperation = new SemaphoreSlim(1, 1);
public static MobileServiceClient client;
public static MobileServiceSQLiteStore store;
public static async Task Initialize()
{
try
{
//This line is not standard for Offline Sync.
//The plugin returns true or false for the devices current connectivity.
//It's my attempt to see if there is a connection, to eliminate the load time.
//This does immediately take it back to the try statement in GetPolicies
if (!CrossConnectivity.Current.IsConnected)
return;
if (client ? .SyncContext ? .IsInitialized ? ? false)
return;
client = new MobileServiceClient(AppSettings.azureUrl);
var path = "local.db"; //Normally uses company ID,
path = Path.Combine(MobileServiceClient.DefaultDatabasePath, path);
store = new MobileServiceSQLiteStore(path);
/************************/
#
region Table Definitions in local SQLite DB
//Define all the tables in the sqlite db
..
store.DefineTable < policy_procedure > ();
..#endregion
await client.SyncContext.InitializeAsync(store);
/************/
#
region Offline Sync Tables
..
policyTable = client.GetSyncTable < policy_procedure > ();
..#endregion
}
catch (Exception ex)
{
Console.WriteLine(ex)
}
}
What I've Tried
Well, I'm not too sure what's even causing this, so most of my attempts have been around forcing an exception before this wait time occurs, so that it can fall out of the GetPolicies try-catch, as the wait time appears to be on the PullAsync.
My most recent attempt at this is commented in the code above (SyncController), where I use James Montemagno's Connectivity Plugin to detect the phones network connectivity. (I've tested this separately, and this works correctly without delay.)
The short story is that you don't want to call PullAsync in your GetPolicies method if your device is offline. For example, you could do
try
{
await SyncController.Initialize();
if (CrossConnectivity.Current.IsConnected)
{
await policyTable.PullAsync("policy_procedure", policyTable.Where(p => p.fk_company_id == companyId).Where(p=> p.signature!=null || p.signature!=""));
}
return await policyTable.ToListAsync();
}
but you will also want to handle the case where this is the first time the app runs and so you don't have any records yet.
I want to broadcast NON-MONGO-DB data from a server publisher to client collections. Currently I save all registered subscriber handles to use those for posting the data
client.js:
col = new Meteor.Collection("data")
Meteor.subscribe("stream")
On server side it looks like
server.js
all_handles = [];
Meteor.publish("stream", function() {
// safe reference to this sessions
var self = this;
// save reference to this subscriber
all_handles.push(self);
// signal ready
self.ready();
// on stop subscription remove this handle from list
self.onStop(function() {
all_handles = _.without(all_handles, self);
}
}
Then I can use the all_handles somewhere in my app to send data to those clients, like:
function broadcast(msg) {
all_handles.forEach(function(handle) {
handle.added("data", Random.id(), msg);
}
}
This is already in use and running.
Q: What I am looking for is: Can I get all handles from currently already existing meteor (internal) objects like _sessions or something else?
It would be great if I had not to organize the subscribers handle all the time by myself.
Please do not answer with links to other broadcast packages like streamy or else. I want to continue with standard collections but with as less code as possible.
Thanks for help and feedback
Tom
Maybe this might work for you: https://stackoverflow.com/a/30814101/2005564
You could get the connections via var connections = Meteor.server.stream_server.open_sockets; but as looshi said this might break with a future meteor update as it is not part of the public API...
As informed by #laberning I used for now the "undocumented" meteor connections.
You can post to all subscribers of a publishing method like:
// publish updated values to all subscribers
function publish_to_all_subscribers(subscription_name, id, data) {
_.each(Meteor.server.stream_server.open_sockets, function(connection) {
_.each(connection._meteorSession._namedSubs, function(sub) {
if (sub._name == subscription_name) {
sub.insert(subscription_name, id, data);
}
})
});
}
// create stream publisher
Meteor.publish('stream', function(){
// set ready
this.ready();
});
...
// use publishing somewhere in your app
publish_to_all_subscribers('stream', Random.id(), {msg: "Hello to all"});
...
updated: See an example MeteorPad for Publish and Subscribe and Broadcast messages
I have the following scenario:
Client side has a button clicking it will execute Meteor.call method on the server-side which will call API and fetch products, During this time I wan't to disable this button + block this method from executing again basically nothing stops you from clicking the button 100x times and server will keep on executing same method again and again.
Few ideas I had in my mind: Use sessions to disable button (Problem: can still using the console Meteor.call and abuse it)
I also looked at Meteor.apply in the docs with wait:true didn't seems to stop from method execution. I honestly not sure how this kind of thing is handled with no hacks.
Client-side:
'click .button-products': function(e){
Meteor.call('getActiveProducts', function(error, results){
if (error)
return Alerts.add(error.reason, 'danger', {autoHide: 5000});
if (results.success)
return Alerts.add('Finished Importing Products Successfully', 'success', {autoHide: 5000});
})
}
Server-side
Meteor.methods({
getActiveProducts: function(){
var user = Meteor.user();
var api = api.forUser(user);
importProducts = function(items){
nextPage = items.pagination.next_page;
items.results.forEach(function(product){
var sameproduct = apiProducts.findOne({listing_id: product.listing_id});
if (sameproduct) {
return;
}
var productExtend = _.extend(product, {userId: Meteor.userId()});
apiProducts.insert(productExtend);
});
};
var products = api.ProductsActive('GET', {includes: 'Images', limit: 1});
importProducts(products);
while (nextPage !== null) {
products = api.ProductsActive('GET', {includes: 'Images', page: nextPage, limit: 1});
importProducts(products);
}
return {success: true};
}
});
From the Meteor docs:
On the server, methods from a given client run one at a time. The N+1th invocation from a client won't start until the Nth invocation returns. However, you can change this by calling this.unblock. This will allow the N+1th invocation to start running in a new fiber.
What this means is that subsequent calls to the method won't actually know that they were made while the first call was still running, because the first call will have already finished running. But you could do something like this:
Meteor.methods({
getActiveProducts: function() {
var currentUser = Meteor.users.findOne(this.userId);
if (currentUser && !currentUser.gettingProducts) {
Meteor.users.update(this.userId, {$set: {gettingProducts: true}});
// let the other calls run, but now they won't get past the if block
this.unblock();
// do your actual method stuff here
Meteor.users.update(this.userId, {$set: {gettingProducts: false}});
}
}
});
Now subsequent calls may run while the first is still running, but they won't run anything inside the if block. Theoretically, if the user sends enough calls, the first call could finish before all of the others have started. But this should at least significantly limit the number of etsy calls that can be initiated by a user. You could adapt this technique to be more robust, such as storing the last time a successful call was initiated and making sure X seconds have passed, or storing the number of times the method has been called in the last hour and limiting that number, etc.
A package I wrote a while back might come in handy for you. Essentially it exposes the Session api on the server side (hence the name), meaning you can do something like ServerSession.set('doingSomethingImportant', true) within the call, and then check this session's value in subsequent calls. The session can only be set on the server, and expires upon connection close (so they could spam calls, but only as fast as they can refresh the page).
In the event of error, you can just reset the session. There shouldn't be any issues related to unexpected errors either because the session will just expire upon connection close. Let me know what you think :)
I am seeing a repeatable issue where a user authenticates ("logs in") with a Meteor server, and then a client subscription that depends on userId is updated (and dependent UI templates reactively update) before Meteor.userId() registers the successful login.
For example, in this code snippet, the assert will throw:
var coll = new Meteor.Collection("test");
if (Meteor.isServer) {
Meteor.publish('mineOrPublic', function () {
// Publish public records and those owned by subscribing user
return coll.find({owner: { $in: [ this.userId, null ]}});
});
}
if (Meteor.isClient) {
var sub = Meteor.subscribe('mineOrPublic');
var cursor = coll.find({});
cursor.observe({
added: function (doc) {
if (doc.owner) {
// This should always be true?!
assert(doc.owner === Meteor.userId());
}
}
});
}
Analogous to the added function above, if I write a template helper that checks Meteor.userId(), it will see a value of null, even when it is invoked with a data context of a document with an owner.
There is apparently a race condition between Meteor collection Pub/Sub and the Account userId update mechanisms. It seems to me that Meteor.userId() should always be updated before any subscriptions update based on a change in this.userId in a server publish function, but for some reason the opposite usually seems to be true (that is, the assert in the code above will usually throw).
The reason I care is because I have packages that depend on obtaining a valid Meteor Authentication token (using Accounts._storedLoginToken()) on the client for use in securing HTTP requests for files stored on the Meteor server. And the authentication token isn't correct until Meteor.userId() is. So the flow of events usually goes something like this:
User logs in
Publish function on server reruns based on the change in this.userId.
Client begins receiving new documents corresponding to the change in userId.
UI Template reactively updates to add DOM elements driven by new documents
Some of the DOM elements are <img> tags with src= values that depend on the data context.
HTTP requests are triggered and ultimately fail with 403 (forbidden) errors because the required authentication cookie hasn't been set yet.
Meteor.userId() finally updates on the client, and code reactively runs to set the authentication cookie
Helpers in the template that depend on a session variable set in the cookie update code are rerun, but the DOM doesn't change, because the URLs in the <img> tags don't change.
Because the DOM doesn't change, the tags don't retry their failed attempts to load the images.
Everything settles down, and the user has to manually reload the page to get their images to appear.
I've come up with two possible approaches to work around this issue:
In the template helper that generates the URL for the <img> tag, always append a dummy query string such as: "?time=" + new Date().getTime(). This causes the DOM to change every time the helper is called and fixes the problem, but it screws-up browser caching and if not coordinated will cause some assets to unnecessarily load multiple times, etc.
In every template helper that touches document data add a test of:
if (this.owner && this.owner !== Meteor.userId()) {
// Perhaps Meteor.loggingIn() could be used above?
// Invalid state, output placeholder
} else {
// Valid state, output proper value for template
}
I really hope someone knows of a less kludgy way to work around this. Alternatively, if consensus arises that this is a bug and Meteor's behavior is incorrect in this respect. I will happily file an issue on Github. I mostly really enjoy working with Meteor, but this is the kind of gritty annoyance that grinds in the gears of "it just works".
Thanks for any and all insights.
After trying lots of things, this variation on the example code in the OP seems to consistently solve the race condition, and I find this an acceptable resolution, unlike my initial attempted workarounds.
I still feel that this kind of logic should be unnecessary and welcome other approaches or opinions on whether Meteor's behavior in the OP sample code is correct or erroneous. If consensus emerges in the comments that Meteor's behavior is wrong, I will create an issue on Github for this.
Thanks for any additional feedback or alternative solutions.
var coll = new Meteor.Collection("test");
if (Meteor.isServer) {
Meteor.publish('mineOrPublic', function (clientUserId) {
if (this.userId === clientUserId) {
// Publish public records and those owned by subscribing user
return coll.find({owner: { $in: [ this.userId, null ]}});
} else {
// Don't return user owned docs unless client sub matches
return coll.find({owner: null});
}
});
}
if (Meteor.isClient) {
Deps.autorun(function () {
// Resubscribe anytime userId changes
var sub = Meteor.subscribe('mineOrPublic', Meteor.userId());
});
var cursor = coll.find({});
cursor.observe({
added: function (doc) {
if (doc.owner) {
// This should always be true?!
assert(doc.owner === Meteor.userId());
}
}
});
}
This code works by giving the server publish function the information it needs to recognize when it is running ahead of the client's own login state, thereby breaking the race condition.
I think this is something that Meteor should do automatically: clients should not see documents based on changes to this.userId in a publish function until after the client Meteor.userId() has been updated.
Do others agree?
I tried with this code that works on server too. In association with FileCollection package.
if (Meteor.isServer) {
CurrentUserId = null;
Meteor.publish(null, function() {
CurrentUserId = this.userId;
});
}
....
OrgFiles.allow({
read: function (userId, file) {
if (CurrentUserId !== file.metadata.owner) {
return false;
} else {
return true;
}
}
...
Dear all,
Template.tmp_detail_campaign_code_batch.events({
'click .ancProdCodePagination': function (e) {
Meteor.subscribe('ItemPage', Number*10,10)
}
});
Its definition:
Meteor.publish('ItemPage', function(skipItem, takeItem){
return Item.find({},{
skip : skipItem,
limit : takeItem
}); }
When I click .ancProdCodePagination, the amount of subscribed Items keep increasing by 10. For pagination, I'd like to make the amount stay at 10, but with different Items for each click.
What should I do?
You just need to stop the previous subscription first, which will involve storing the handle that it returns somewhere:
var itemSub;
Template.tmp_detail_campaign_code_batch.events({
'click .ancProdCodePagination': function (e) {
if (itemSub)
itemSub.stop();
itemSub = Meteor.subscribe('ItemPage', Number*10,10);
}
});
From the docs, the stop method does this:
Cancel the subscription. This will typically result in the server directing the client to remove the subscription's data from the client's cache.