-
Notifications
You must be signed in to change notification settings - Fork 173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"FCModel 2" non-unique-instances branch #98
Comments
Most of these changes seem great, detached instances is just a much simpler model, and we had some truly weird bugs caused by the uniquing. However I'm curious what the rationale for killing We rely on should/did persist to update some cache columns and do some full text search related changes: -(void) didPersist {
[SeachThingy addOrUpdateDataForAudioItem:self];
} As for #define ASSERT_SAVE(obj) NSAssert2([obj save] == FCModelSaveSucceeded, @"Failed to save object %@ at line %i", obj, __LINE__) The vast bulk of our save calls don't call it. |
@NZKoz The main goals for the
For instance, I have an OCFeed model in Overcast. It can exist in two modes defined by an - (BOOL)shouldInsert { return ! self.isTransient; }
- (BOOL)shouldUpdate { return ! self.isTransient; }
- (BOOL)shouldDelete { return ! self.isTransient; }
- (void)didDelete
{
[OCFeedItem deleteAllItemsInFeedID:self.id];
} Here's the new code under "FCModel 2": - (BOOL)save
{
if (self.isTransient) return NO;
return [super save];
}
- (void)delete
{
[super delete];
[OCFeedItem deleteAllItemsInFeedID:self.id];
} You can still do everything you could do before, but I think it makes more sense this way, especially if you're new to FCModel or you've forgotten how it works since you wrote the code 6 months ago and don't know to look for those should_/did_ methods when you're trying to figure out why something's behaving weirdly. |
I'm neither here nor there on the The callbacks for However I can speak from experience that providing developers with those callbacks will be both a blessing and a curse, there's tonnes of brittle rails code caused by callback-mystery-wtf-happenedcode. |
One other thing, your decision to not try and rollback object state on transaction failure is the right one. We do that and it's essentially impossible to do correctly leading to buggy code with 500 special cases. |
Anything I can do to help move this forward? I'm going to want to use non-unique instances (right approach and we actually have an extension writing to the db). Are there gotchas or general directional things you'd rather not see? |
Sorry, I've been busy trying to get the latest Overcast update out the door. I'm going to be tackling this in the next major update. I'll be writing the FCModel2/whatever system along with it, probably a couple of months from now. I have a branch of the Overcast code running with this FCModel branch, and there are still a few big things I'd like to change. |
Made a few improvements tonight:
|
You could probably support variadic methods and keep That said, NSArray is a lot safer than varargs. I'll miss FCModel's variadic methods, but not "a lot" :) |
I already hate it without variadics. Putting them back. |
Just committed fc5b468 with restored variadic I also removed the "keyed" versions that returned dictionaries (too much code complexity/duplication for too little gain) and the functions that operated on existing |
@marcoarment I've played around with my own suggestion now and I'm not happy with it. I thought it'd be clever to drop all NSArray variants and use only The whole But some methods like And since you have already added NSArray variants for all FCModel methods, there's probably no good reason to add |
Thanks for looking into that. I just removed the va_list variants. Also added basic in-memory caching by primary key (but using copies, not shared instances) and a few other fixes. Here's another (crazy?) idea: Can/should we drop the custom serialization/unserialization for things like NSDate and NSURL and just do basic strings and numbers (which FMDB does anyway)? Conceptually, it has always been problematic (and a big source of bugs — see all of the pull requests on those) because SQLite doesn't naturally handle those datatypes, and there's issues like what happens if a non-NULL column reads in an invalid URL string from the database that results in a nil NSURL. I'd love to get rid of it and just leave devs to implement their own serializations as necessary in |
NSDate handling is the single most common crash in our app. I'm 100% fine w/ keeping them as strings. If we need to, we can always provide a helper method. |
On top of that, you could provide an update in the README showing how a simple category could be used to add serializing OR, even sweeter, allow a way for use of Mantle or similar. |
Alright, I'm going to run my usual test on serialization removal: trying to convert Overcast to it and seeing how much it sucks. |
👍 |
It was actually pretty easy (9173939). What do you think? |
I'll test it out first thing in the morning but with a cursory view it looks good. |
Could |
I don't know — I think the whole concept was flawed. Object properties that map to database columns should always reflect what those column values are or should be. I think the role of serialization is better left to implementors and custom methods. |
That's a reasonable point about object properties. But I'm thinking about providing the serialized accessors automatically via +resolveInstanceMethod:, does that make sense for FCModel? |
That's certainly a fair point, though it's not uncommon for model frameworks to do trivial value transformations. The downside of not having transforms is that you push statefulness down to subclasses. Unfortunately, it adds a lot of boilerplate code for common patterns.
It's not terrible, but it's additional work that could be avoided if subclasses could perform transformations when a snapshot is made to generate the INSERT or UPDATE. |
FWIW, I just fixed the last known Overcast bug with using this branch — doing the conversion was surprisingly easy. There were only a few spots in the code where I was relying on instances being unique in memory. I'm going to ship the next update with this codebase after a (hopefully brief) beta. I think it's time to give this a home and make it official. What do you think is the right way to do this? New "FCModel2" project? Or just release it as v2.0 and hope people are paying attention? |
If people are using cocoapods correctly, they'll be manually opting in to the update. It'll only get bumped if they say
I'd just push the 2.0 version, but maintain a 1-0-stable branch or something on the off chance that people notice a bug and want it patched. |
I agree with @NZKoz because then if you absolutely need to use an older version you still can. |
I think that the framework should be able to offer support for easy values transformation. As @lickel mentioned above, this is a highly repetitive task. Indeed not having it is not terrible but annoying and for me it is close to bad. This is related to another aspect: for the time being the data model is created through inspection of the database table and class structures. This is not always very confortable. It would be nice to declare a sort of descriptor for a given pair class/table that is able to map attributes to columns. This descriptor may also declare the transformers. But before this descriptor, restoring the ability to transform data is (in my opinion) something really necessary. |
I agree with @mirion that value transformations would be nice to have. Even though I removed my one implementation of I think an approach similar to how CoreData handles this could turn out nicely. Basically it allows you to specify a NSValueTransformer, and by default uses a transformer that knows how to handle objects implementing The |
I just went to open a ticket asking about typical expected FCModel concurrency patterns, and found this. My two cents:
I wonder if Overcast is already shipping FCModel 2.0? Any particular reason why we shouldn't ship our apps with it as well, provided that we're prepared to debug the problems? |
Oh, one more thing. I'm using those keyed access methods a lot; am I the only one? Of course, this is a minor issue (I can use my collection grouping categories, or I can add a category to FCModel), but I wonder why @marcoarment finds them to bring “too little gain”. My use case is bulk-fetching of data (often on a background queue) before processing it (on the main queue). |
Regarding NSValueTransformer things: CoreData uses, by default, a reversed version of |
How's this looking @marcoarment? I've been riding on "1.0" but am seeing some bad performance for larger data sets (really not that big, less than 100 objects) and hoping this could help. Any updates? |
Dude, integrated non-unique and the performance went from taking 40 seconds to save everything to 1.4s. Yummy! |
In fact, your problem is triggered by the huge number of notifications that are generated by FCModel. Just modify the code to disable the notifications. II already have an implementation, if you need it, let me know and I will make a pull request. |
The timing issues were because of |
@marcoarment are you still maintaining FCModel 1? If yes, I have a several corrections into my fork. If you want to integrate something, please let me know to make PRs. |
No, I'm on non-unique now and not turning back! :-D |
@johncblandii Indeed everything starts with dataWasUpdatedExternally, but dig deeper, you will see that the problem is eventually produced by the notifications. I profiled the code. In my opinion, (at least for my use case), v2 has some big flaws and is not ready for prime time. Your experience my be different. |
It isn't a full release so I expect some issues (like the dropping of serialization which I'm addressing now) but overall the performance is worth it. Seeing as Overcast is using it in a large way, I'm confident I can make it work for this app. |
@mirion: I don't plan any more updates to v1 — v2 is the way forward. I'd love to hear the flaws you've identified in it. |
@marcoarment
There were others, but I can't remember right now. Will write again if I'll remember. Regarding v1, I have some corrections/improvements that may be useful for v2: |
I'm curious @mirion, are you trying to use a remote value as a PK? |
Sounds like we just disagree on the meaning of "has some big flaws and is not ready for prime time". I took that to mean bugs, while you appear to be referring to features and implementation details on which we disagree. |
@marcoarment Do you have a sense for when the non-unique branch will be ready for production use? We're anxious to switch over but have been holding off for the final changes to be done before we do. Is the branch pretty stable as is or should we keep holding off for a while longer? Thanks for all the work you've done on this, we've really enjoyed using FCModel in our apps. |
@lynns I haven't had time to formally publish and document it as such, but I consider this branch stable for production. I've been shipping it in Overcast for months with no problems. Once WWDC settles down, I'll make it formal. |
@marcoarment Sounds good to me. We'll start trying it out and see how it goes for us. Thanks! |
@lynns I ported to non-unique last night and I can't speak highly enough about it. You'll need to address serialization (I just used It's well worth at least a spike. @marcoarment, I think the community could help document it. Hopefully these projects wind down in the next week or so and I can lend a hand in that area. |
Please don't understand me wrongly. My words may sound too strong, I really think that you did a great job with this library. I like it, I'm using it and thanks a lot for all your work. If I'm commenting your decisions is not to criticize you, but just because I like your code, I want to use it in the future, therefore I want to see it in the best shape for as many of us (including for advanced use), not just for some cases. I used the word "flaw" because some design decisions (every api that was removed represent in the end a design decision) are beyond the term "bugs". Of course, these design decisions are in the end easy to revert and solve the bugs ;-) @johncblandii I had a problem (in v1) with records sent from the server, where because of some networking problems, the server was pushing them two times. Just a pipe problem, completely unpredictable. |
|
@prendio2 Ah, that's legacy. The notifications used to track which instances changed and would include them in the notification payloads, but they don't anymore. |
@marcoarment did they ever do that on this non-uniques-instances branch? The initial commit e0f9b3d doesn't use them in the userInfo dictionary either. Would you be open to having them in the main repo if I (re)implement it and pull request or is that something you'd prefer I kept in my own fork? |
@prendio2 No, this branch never included them. I'd like to keep them out — the rationale was not only to improve performance on changes and reduce memory overhead, but to reinforce the non-unique-instances design pattern, in which you're intended to always manage your own instances on the caller side and never assume any one instance is shared (ever) or up-to-date (outside of a reloadAndSave block). |
@mirion, I store all remote |
@marcoarment OK. My rationale is I want to get the id of the changed object to avoid doing a complete reload each time any object changes. Maybe I'll add an id to the user info dict rather than the FCModel object itself. |
@johncblandii That was just an example of error that is effectively unpredictable. If the app crashes because the code raises an exception, this doesn't give any good impression. Such an error should be recoverable. In my case I just changed the table definition to use on conflict replace. Problem solved, even without error handling (via return nserror) but we can't conceive all these edge cases that are completely out of our control. |
@marcoarment You mentioned previously that you are considering removing external access to |
After banging my head against the wall trying to solve some obscure and tricky Overcast bugs related to non-unique-instances for months, I've decided to try a different approach in the new unique2 branch: it's effectively all of the other improvements from non-unique-instances, but with two big changes:
Separately, these are weird and can be problematic. Together, I think they make a lot more sense. Unique instances allow far simpler, faster, and more efficient code for many tasks, but hit weird issues in FCModel 1 with the instance's field values changing from another thread in the middle of an operation. The recent introduction of Maybe there's something I'm missing here, but I'm trying this in Overcast betas this summer and will report back. |
@marcoarment Any updates on how your |
@lynns: I'm using it exclusively now, and about to ship Overcast 2.0 with it. So far, it's rock-solid — better than 1.0 and non-unique-instances. |
Now that you (and maybe others?) have been using this for a while, would you consider releasing this as a FCModel 2.0? |
Just pushed the first commit e0f9b3d of what might be called "FCModel 2" to the "non-unique-instances" branch. This is a big, likely-breaking change that applies a lot of lessons learned since the original design:
Instances are no longer tracked or kept unique in memory. All instances are "detached".
dataWasUpdatedExternally
, reloads, and conflict resolution are no longer necessary and have been removed.allLoadedInstances
is no longer possible and has been removed.All saves and deletes are expected to succeed. Saves shouldn't unexpectedly fail or be blocked.
should/didInsert/Update/Delete
,saveWasRefused
,saveDidFail
. To customize behavior, overridesave
and call[super save]
from the subclass.FCModelSaveResult
is removed.delete
now returns void andsave
now returns a BOOL to indicate whether changes were made.lastSQLiteError
is removed.FCModelException
.saveAll
has been removed. Saves should happen intentionally, right after you change the data.NSURL
,NSDate
, etc. have been removed to avoid a lot of subtle bugs and inconsistencies between formats.Notifications have been simplified to just a single FCModelChangeNotification for any change to a table.
FCModelInstanceSetKey
has been replaced byFCModelInstanceKey
and will always be one instance if known, or unset for many/unknown.Take a look and see what you think so far. What other changes should we consider now that we're breaking things?
PLEASE don't ship any apps with this yet.
The text was updated successfully, but these errors were encountered: