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

RDART-1020: Fix writeAsync behaviour #1666

Merged
merged 7 commits into from
May 30, 2024
Merged

RDART-1020: Fix writeAsync behaviour #1666

merged 7 commits into from
May 30, 2024

Conversation

nielsenko
Copy link
Contributor

@nielsenko nielsenko commented May 6, 2024

This PR has been edited to simply disallow async callbacks in all cases, even to writeAsync.


Calling Realm.writeAsync with an async callback is currently broken, since the callback is not awaited, and not passed as a FutureOr<T> Function().

The first commit adds tests thats illustrates a number of issues with the current approach, and the second fixes them.

We could consider detecting re-entrancy, and either throw an exception, or simply allow it by participating in the calling transaction, but this is not handled by the current PR.

Fixes: #1667

@nielsenko nielsenko requested a review from nirinchev May 6, 2024 16:40
@cla-bot cla-bot bot added the cla: yes label May 6, 2024
@nielsenko nielsenko changed the title Fix writeAsync behaviour RDART-1020: Fix writeAsync behaviour May 6, 2024
@nielsenko nielsenko marked this pull request as ready for review May 6, 2024 17:01
Copy link
Member

@nirinchev nirinchev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should do that. We're intentionally guiding people toward using the sync API because holding the write lock for extended periods of time is an antipattern and can result in app freezes in multithreaded/process scenarios and can usually be avoided by doing the async calls before entering the write transaction.

My suggestion would be to instead check if T is a future and log a warning or even error out.

@nielsenko
Copy link
Contributor Author

I don't think we should do that. We're intentionally guiding people toward using the sync API because holding the write lock for extended periods of time is an antipattern and can result in app freezes in multithreaded/process scenarios and can usually be avoided by doing the async calls before entering the write transaction.

My suggestion would be to instead check if T is a future and log a warning or even error out.

I disagree.

We already expose beginWriteTransaction and friends, so users have all the foot-guns they need available already. writeAsync is just a 10-line wrapper around these.

If we force people to not use async callbacks in writeAsync they will just start using those APIs directly. I think it is much better to make writeAsync behave sane, and just keep warning against long running transactions. We can definitely ensure that writeAsync never blocks, as long as the callback don't.

We should however make such a check in write. Today you can pass an async callback unhindered to write, which affords no sane semantics.

@nielsenko nielsenko requested a review from nirinchev May 6, 2024 19:42
@nirinchev
Copy link
Member

Yes, I know it's not difficult to do it and I don't think we should be super actively preventing people from doing it, but I want to make it harder for them to shoot themselves in the foot. The writeAsync API is the default one people are likely to use, so if that has a future as its argument, it'll imply that we condone or even encourage doing async work in the callback. Instead, my preference would be to throw in case you pass in a future and if someone complains, explain they could achieve the same with the begin/commit API. Happy to bounce ideas about it at some point this week though as it's mostly a cautious preference rather than a strong conviction against it.

try {
T result = writeCallback();
T result = await writeCallback(); // ignore: await_only_futures
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we await here if we assert the callback isn't a future?

Copy link
Contributor Author

@nielsenko nielsenko May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄 ever vigilant. I could just drop it. As is, all that is needed to support async callbacks is to change the return type of writeCallback from T to FutureOr<T> .. and re-enable some skipped tests.

I should probably have put this PR back into in-progress

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.. but it is not a bug per-se .. in dart you can await non-futures, even null just fine. It differs from .NET on that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed all the new tests again and simply disallowed async callbacks even to writeAsync.

@nielsenko nielsenko marked this pull request as draft May 30, 2024 09:53
@nielsenko nielsenko marked this pull request as ready for review May 30, 2024 10:01
@nielsenko nielsenko requested a review from nirinchev May 30, 2024 10:01
Copy link

Pull Request Test Coverage Report for Build 9300668832

Details

  • 4 of 4 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.008%) to 86.992%

Totals Coverage Status
Change from base Build 9300439599: 0.008%
Covered Lines: 5905
Relevant Lines: 6788

💛 - Coveralls

@nielsenko nielsenko merged commit 641956d into main May 30, 2024
57 of 58 checks passed
@nielsenko nielsenko deleted the kn/use-futureor branch May 30, 2024 15:08
nirinchev added a commit that referenced this pull request Jun 6, 2024
* main:
  RDART-1020: Fix writeAsync behaviour (#1666)
  RDART-999: Fix flutter test dlopen (#1623)
  RDART-1045: Expose setrlimit ios (#1700)
  RDART-962: Use xcode 15.4 (#1548)

# Conflicts:
#	packages/realm_dart/lib/src/cli/install/install_command.dart
nirinchev added a commit that referenced this pull request Jun 12, 2024
* main:
  Add vNext Changelog header (#1717)
  [Release 3.0.0] (#1716)
  libraryVersion moved to realm_libary.dart (take 2)
  libraryVersion moved to realm_libary.dart
  Update CHANGELOG (#1715)
  Github composite action for setting up flutter on runner (#1710)
  RDART-866: kn/decimal128 web support (#1713)
  Reduce expected gain of memEquals for test stability
  Refresh after awaiting download to stabilize tests
  RDART-866: Minimal web support (#1699)
  RDART-1052: Update realm-core to v14.9.0 (#1704)
  RDART-1020: Fix writeAsync behaviour (#1666)
  RDART-999: Fix flutter test dlopen (#1623)
  RDART-1045: Expose setrlimit ios (#1700)
  RDART-962: Use xcode 15.4 (#1548)
  RDART-1039: Drop catalyst support. Flutter doesn't support it (#1696)

# Conflicts:
#	packages/realm_dart/src/realm-core
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Realm.writeAsync do not handle async callbacks correctly
2 participants