method timeout instead of waiting - breeze

It would appear that the savechanges method on breeze waits indefinitely when calling to or waiting for the server. Is there a way of getting it to time out? I am calling save change with allowConcurrentSaves: false. This now causes users who somehow do not get a response from the server to simply hang in limbo indefinitely say for example with a dropped internet connection.
I do not want to re-call the method with allowConcurrentSaves to false fearing that I might duplicate the data.
Any ideas?
Thanks

Update 16 May 2014
You can set HTTP-level timeout and cancellation with the AJAX Adapter's requestInterceptor as of v.1.4.12. See the documentation, "Controlling AJAX calls".
I'd still be reluctant to use this feature on save as you have no chance of knowing what whether the server persisted the data or not. Of course if your client hangs or crashes you don't know anyway. It's up to you.
Original Answer
Actually, there is a ready-made solution from Q.js. It's called timeout and it's mentioned in the API reference with a simplified example of its implementation and use in the readme.md.
I know you asked about Save but your question is pertinent for promises in general. Here is a query example adapted from the queryTests.js in our DocCode Sample
var timeoutMs = 10000; // 10 second timeout
var em = newEm(); // creates a new EntityManager
var query = new EntityQuery().from("Customers").using(em);
Q.timeout(query.execute, timeoutMs)
.then(queryFinishedBeforeTimeout)
.fail(queryFailedOrTimedout);
function queryFailedOrTimedout(error) {
var expect = /timed out/i;
var emsg = error.message;
if (expect.test(emsg)) {
log("Query timed out w/ message '{0}' " + expectTimeoutMsg)
.format(emsg));
// do something
} else {
handleFail(error);
}
}
Note: I just added this test so you'd have to get if from github or wait for a Breeze release after 1.2.5.
Oops ... maybe not
I gave what I think is a great answer for query. It may not be the right answer for save.
The problem with save is that you do not know on the client if the save succeeded until the server responds. Things could go wrong anywhere along the way. The server might not have heard the request to save. The server may have failed during save. The server may have saved the data but the response never made it back to the client.
Changing the value of allowConcurrentSaves won't get you out of this bind. Neither will having a save timeout.
In fact, adding a timeout to the save is probably deceiving. It is even possible for the save response to arrive after your custom timeout ... in which case Breeze will have tried to update your EntityManager ... and you won't know if Breeze succeeded or failed!
What if we added a Breeze save timeout. What should it do? What if breeze said the save had timedout ... and Breeze ignored a belated response from the server? Then imagine that the save succeeded on the server - it just took "too long" for it to respond to the client. Now you've got a client whose state is unexpectedly out of sync with the server. This is not good.
So I think you want a different solution to this very real problem. It's a user experience problem really. You can indicate to the user that you think the save is still in progress and then set your own timer. If the save isn't done when your timer expires, you can query the server to see if the data have been saved or if there is a connection ... or something along these lines. I can't think of a better way right now honestly.
Note that I'm assuming you need to know that the server succeeded. If you avoid store-generated IDs and always assume saves succeed unless the server tells you otherwise ... well that's a completely different paradigm and programming model that we could talk about someday (see meteorjs).
The net of all of this: I'm pretty darned sure that a save timeout is NOT what you want.
Still useful on a query though :)

Great question, and I wish I had a good answer. But it is definitely worth looking into. Could you please add this as a feature request to the Breeze User Voice. We take these requests very seriously in determining our priorities for Breeze development.

Related

Testing CKErrorChangeTokenExpired handling by generating known expired CKServerChangeToken

The header comment on CKFetchDatabaseChangesOperation fetchDatabaseChangesCompletionBlock states:
"If the server returns a CKErrorChangeTokenExpired error, the previousServerChangeToken value was too old and the client should toss its local cache and
re-fetch the changes in this record zone starting with a nil previousServerChangeToken."
I would like to test this scenario thus I would like to generate an expired CKServerChangeToken so I can set it as the previousServerChangeToken on a CKFetchDatabaseChangesOperation.
I added an init method from the private header:
#interface CKServerChangeToken (Private)
- (id)initWithData:(NSData *)data;
#end
And used it as follows:
CKServerChangeToken knownExpiredToken = [[CKServerChangeToken alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:#"AQAAAVl57tUGHv6sgNT9EeaTcQCM+sDHHA==" options:0]];
That string is a valid change token returned from a request and I have tried unsuccessfully modifying it, e.g. reducing numbers that I see incrementing to lower ones. I have however managed to get another strange invalid argument errors like continuation marker missing. I would be grateful if a CloudKit engineer has any suggestions, thanks.
This is an old post, but I've unintentionally generated .changeTokenExpired errors by doing this:
Fetch changes and save your change token.
In the dashboard, delete the zone you're syncing with.
Create a new zone with the same name.
In your code, fetch changes again, using the change token from step 1.
Since the token refers to a different zone, it doesn't make any sense to CloudKit, which returns a .changeTokenExpired error.
It would be really nice if Apple provided a way to do this, because it's an absolutely crucial situation to test.
I couldn't find any recommended way to do this, so I used tokens generated for other zones. If you've got multiple zones like I did, you can effectively cross wire token retrieval. It's not an elegant solution, but it does work... :)

Getting Distinct or FirstOrDefault with Breeze Client-Side

A query already executed with breeze on the client side and I store the results locally. I was really hoping to either .Distinct() or .FirstOrDefault() on the client side so that I can execute locally instead of going back to the server.
I know that breeze has limited capabilities on the client side. I've looked through the samples and no luck there.
http://www.breezejs.com/documentation/query-examples
Can breeze do this? Is this something that they will be doing in the future?
There's nothing yet built into breeze that will do this. I've run into the same thing, and had to call .Distinct() and .FirstOrDefault() on the server side. There is an outstanding suggestion from last year in the breeze forums (currently at #10 on the list by vote count) to add .Distinct()-like functionality.
I need to see your query to answer properly.
In almost all cases, take(1) is equivalent to firstOrDefault and works fine on both remote and local queries.
var query = breeze.EntityQuery.from('Persons')
.where('FirstName', 'eq', 'Lizzy')
.take(1); // will return one or null
If you want to find an entity by its key, looking first in the cache and then going remote if necessary, then you should consider the fetchEntityByKey method.
Distinct only makes sense for projection queries in which the objects returned (which are NOT entities, btw) have no id. Can't know if that is what you need until you show me your query.
With due respect, I feel that hunch_hunch's answer is a bit misleading. Please reconsider checking MY ANSWER as the best answer.

Is NSData + (id)dataWithContentsOfURL:(NSURL *)aURL options:(NSDataReadingOptions)mask error:(NSError **)errorPtr: cached automatically?

When I read the section on
NSDataReadingOptions
Options for methods used to read NSData objects.
enum {
NSDataReadingMappedIfSafe = 1UL << 0,
NSDataReadingUncached = 1UL << 1,
NSDataReadingMappedAlways = 1UL << 3,
};
typedef NSUInteger NSDataReadingOptions;
It says that
NSDataReadingUncached
A hint indicating the file should not be stored in the file-system caches.
For data being read once and discarded, this option can improve performance.
Available in OS X v10.6 and later.
Declared in NSData.h.
So I am assuming that by default these URL requests are cached and there is no need to implement NSURLRequest to cache data if I want to use shared global cache ? Is this understanding correct ?
Let me start off by saying that dataWithContentsOfURL:options:error: and its ilk are probably the worst APIs for getting something from network. They are very alluring to developers because they can get a resource from network in a single line of code, but they come with some very pernicious side-effects:
First, they block the thread on which they are called. This means that if you execute this on the main thread (the only thread on which your UI can be updated), then your application will appear frozen to the user. This is a really big 'no no' from a user experience perspective.
Second, you cannot cancel these requests, so even if you put this request on a background thread, it will continue to download even though the data may no longer be useful. For example, if your user arrives on a view controller and you execute this request and the user subsequently decides to hit a back button, that data will continue to download, even though it is no longer relevant.
Bottom line: DO NOT USE THESE APIs.
Please use async networking like NSURLConnection or AFNetworking. These classes were designed to get data efficiently and in a way that doesn't impact the user experience. What's even better is that they handle the specific use case you originally asked: how do I stop it from caching on disk?.
There is no answer to your specific question which refer to the caches managed by the URL loading system.
The reading options in method dataWithContentsOfFile:options:error: refer solely to reading from the file system (please consult to the official documentation of NSDataReadingOptions).
Unfortunately, the documentation gives no hint about the behavior when reading from remote resources. Whether the response is cached in the URL cache is unspecified - that is, we don't know, and the internal behavior and implementation can change with each new OS version.
IMHO, we should generally avoid to use the "convenient methods" to read from remote resources. It appears, these methods work only by "accident" when accessing remote resources. So, I strongly agree with #Wayne Hartman ;)

Issue using executeQueryLocally without server metadata

I'm not using EF, so have followed the NoDb sample to successfully load data from my WebApi without using the server side metadata. After the initial load, I was hoping to use the local data cache in the EntityManager while the user interacts with the page. The problem is when I call executeQueryLocally, the cached data set is empty. I stepped through the code to see why the data wasn't being saved to the cache, and there were two issues:
in _getEntityType, metadataStore.isEmpty() was returning true.
in _getEntityType, metadataStore._getEntityTypeNameForResourceName was returning nothing
To get around the this, I added calls in my code to metadataStore.addDataService and metadataStore._setEntityTypeForResourceName. After adding these, the cache was saved properly and executeQueryLocally worked. I'm assuming this was not the intended way to get this to work... Is there something else I am doing wrong? Or is this a bug that can be fixed?
Sorry for taking so long getting back to this one.
We just made the metadataStore.setEntityTypeForResourceName public in breeze v.1.1.3. ( we renamed the method to remove the first '_".
Otherwise, you did exactly the right thing. Good catch.

Railscast doesn't recommend a solution for production, I'm looking for a reason why

In this railscast our good friend Mr. Bates walks through a solution to creating an app that can search, sort, and paginate a set of data. When going through AJAX searching he provides a solution that will display results of the search the moment a user enters input into the search box. Here is his solution:
$('#products_search input').keyup(function () {
$.get($('#products_search').attr('action'), ↵
$('#products_search').serialize(), null, 'script');
return false;
});
However he states "Note that this is only a quick demo and isn’t the best way to do this. There are several jQuery plugins that you can use if you do something like this in a production app." I'm looking for an explanation on why he believes this isn't suitable for production. Thanks in advance!
There are two major issues I see with this solution. The first is that you are making an HTTP (AJAX) request every time a key is pressed, which will not be the most efficient way of doing this. The second is that you are basically calling eval in the response, and eval is bad as it can lead to malicious users executing code you don't want to be executed.
Some suggestions on improving:
Use a proper JSON parser and pass the data back as JSON. (you can use $.getJSON)
Throttle the request - don't do it on every keyUp, maybe start a timer and only submit the request if no keys have been pressed in the last second, meaning it won't make lots of calls for people who type fast.
Cache the response. If you have already searched for something, then there is no point fetching the data twice. Keep a note (in a JS Object) of previous calls in this session and their results.

Resources