Skip to content
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

Obviate long-lived transactions in data binding #647

Closed
fealebenpae opened this issue Jun 17, 2016 · 14 comments
Closed

Obviate long-lived transactions in data binding #647

fealebenpae opened this issue Jun 17, 2016 · 14 comments
Assignees

Comments

@fealebenpae
Copy link
Member

fealebenpae commented Jun 17, 2016

In order for MVVM data binding in XAML to work we need to keep around a write transaction so that setters invoked by binding will execute.

However, long-lived transactions have a serious drawback in that they block writes on other threads or nested writes. We do not want the property setters on a RealmObject to be smart enough to create implicit write transactions as that would be a performance hit in all other cases save data binding.

I propose that we make RealmObject implement System.Reflection.IReflectableType, which Xamarin.Forms respects, so that the custom PropertyInfo instances our implementation yields would be smart enough to open a write transaction if there is none. This would simplify the architecture of MVVM apps using Realm by removing the need for the long-lived transaction pattern without incurring any performance penalties for uses of RealmObject in cases other than data binding.

@AndyDentFree
Copy link
Contributor

Does it auto-close it?

I suggested a few days ago that I'd like an option which created a write transaction in the setter call that lived long enough to write the value.

I think that needs to be an explicit mode people set - I'd be very worried about something creating write transactions invisibly by default because of the potential for clashes. In particular, any kind of auto-transaction on a thread needs to close ASAP to avoid clashing.

@AndyDentFree
Copy link
Contributor

As a general goal, yes I want us to deliver data binding with the least possible code especially anything varying from XAML-based idioms.

@fealebenpae
Copy link
Member Author

fealebenpae commented Jun 17, 2016

Does it auto-close it?

Yes, the set method returned by the custom PropertyInfo implementation would look something like this:

if (obj.Realm.IsInTransaction)
    original.Invoke(obj, new object[] { value });
else
    obj.Realm.Write(() => original.Invoke(obj, new object[] { value }));

where obj is a RealmObject and original is a MethodInfo.

@AndyDentFree
Copy link
Contributor

Works for me. All we need to do is add UWP and a lot of people will be happy!

@AndyDentFree
Copy link
Contributor

How does this align with moving away from RealmObject as a parent? What is the relative priority?

@fealebenpae
Copy link
Member Author

I believe it's something we want sooner rather than later, as it makes it easier to use two-way databinding in Xamarin.Forms apps, especially when you drill down into relationships.

@fealebenpae fealebenpae added this to the v1.0 milestone Jul 5, 2016
@ericmutta
Copy link

I justed asked about this issue on StackOverflow:

http://stackoverflow.com/questions/38394180/realm-mvcc-and-long-running-transactions

One possible solution is to add a Realm.Unmanage() method which does the opposite of Realm.Manage() ...i.e it would copy the current values from the database to the backing fields, then "release" the object so that setting properties outside a transaction does not fail. Later you can call Realm.Manage() again to save the current values.

With that in place, data binding would go as follows:

  1. Load the object you want.

  2. Unbind it from Realm via Realm.Unmanage()

  3. Hook it up for data-binding.

  4. When user hits save, just call Realm.Manage() within a write transaction and go back to step 2.

For convenience we might even have a Realm.Save() method which takes an unmanaged object, calls Realm.Manage() in a write transaction, then calls Realm.Unmanage() again so it can keep being used in a data-binding scenario.

Food for thought!

@AndyDentFree AndyDentFree self-assigned this Aug 1, 2016
@AndyDentFree
Copy link
Contributor

@fealebenpae just pointed out this issue should be accomplished by implementing ICustomReflectible with a TypeDelegator descendant after I commented it may need IL skills.

I think my impression of it needing weaving work was from an earlier idea I had about this being done after or concurrently with moving from RealmObject to an interface.

@AndyDentFree
Copy link
Contributor

From discussions in our Engineering (internal) Slack channel, we should also be aiming to reduce the number of transactions between the oldest and latest Write or (implicit) Read transactions in a Realm instance, to avoid impeding space reused.

The core calls that cull old read transactions are advance_read and promote_to_write.

@AndyDentFree AndyDentFree removed their assignment Aug 16, 2016
@AndyDentFree AndyDentFree removed this from the v1.0 milestone Aug 16, 2016
@AndyDentFree
Copy link
Contributor

One reason for reducing the priority of this issue is that it loses the ability to implement a Cancel button by simply aborting a transaction. If we commit per binding change, rollback requires a separate copy somewhere.

@UKDeveloper99
Copy link

I'd like some clarity on this issue as I feel it may effect me in the somewhat near future. I rely heavily on Mvvm (MvvmCross) in my Xamarin projects that use Realm. Currently I've only made databindings to RealmObjects that display the data (Read-only). I know of some work coming up that will require a databound RealmObject to be updated from the databinding set. For example a user edits some data in an EditText control. This will be a Two-way binding so will set the data in the RealmObject will have it's setter called.

Does this mean that an exception will occur at this point because it's not in a write transaction? Do I need to have an intermediary object and bind to that instead and then sync that object with the Realm database every so often. Maybe use something like AutoMapper?

@kristiandupont
Copy link
Contributor

Implemented in #901

@AndyDentFree
Copy link
Contributor

Note from testing in 2018, the way bindings are currently working, the setter is called per-character typed into a field, so this is generating a lot of transactions.

@nirinchev
Copy link
Member

This actually depends on the UpdateSourceTrigger. Unfortunately, it seems like Xamarin.Forms doesn't expose a way to control that and it only uses PropertyChanged which is the most aggressive one as you've observed. You can probably alleviate that by subclassing the Entry property and debouncing the property changed event invocations.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants