cachapa / firedart

A dart-native implementation of the Firebase Auth and Firestore SDKs
https://pub.dev/packages/firedart
Apache License 2.0
174 stars 62 forks source link

feat: add transactions #146

Open jsgalarraga opened 4 months ago

jsgalarraga commented 4 months ago

This PR introduces Firestore transactions to the package.

Why

Transactions are a powerful feature to have in the backend. This is an effort to have a more complete feature set for a dart backend.

It has been requested by users in #63 and #126.

Inspiration

The work here has been inspired by:

Implementation notes

Examples

To ensure transactions are working and keeping the database consistent with all the write operations performed in parallel, the following test has been run: Increase a value multiple times both with and without transactions. As we can see, when running with transactions, the numbers are increased correctly 3 times, but the number is only increased by 1 when ran without transactions.

https://github.com/jsgalarraga/firedart/assets/52668514/559a8fa7-62ec-4a46-a41e-fbcb565392ce

With transactions

void main(List<String> args) async {
  Firestore.initialize('project-id');

  await Future.wait([
    increaseValueWithTransaction(Firestore.instance),
    increaseValueWithTransaction(Firestore.instance),
    increaseValueWithTransaction(Firestore.instance),
  ]);

  firestore.close();
}

Future<void> increaseValueWithTransaction(Firestore firestore) async {
  await firestore.runTransaction(
    (transaction) async {
      final doc = await transaction.get('testing/test');
      final value = doc.map['key'];
      transaction.update('testing/test', {'key': value + 1});
    },
  );
}

Without transactions

void main(List<String> args) async {
  Firestore.initialize('project-id');

  await Future.wait([
    increaseValue(Firestore.instance),
    increaseValue(Firestore.instance),
    increaseValue(Firestore.instance),
  ]);

  firestore.close();
}

Future<void> increaseValue(Firestore firestore) async {
  final doc = await firestore.document('testing/test').get();
  final value = doc.map['key'];
  await firestore.document('testing/test').set({'key': value + 1});
}
evandrobubiak commented 1 week ago

Any predictions for this merge? We are really looking forward to this feature

cachapa commented 1 week ago

Hi @evandrobubiak, sorry for allowing this to stale, I must've missed the original PR email.

I'll review this now.

cachapa commented 1 week ago

Also, would you mind writing a few tests? It doesn't need to be comprehensive, but at least covering the basics, essentially the samples you wrote for the documentation.

jsgalarraga commented 1 week ago

Oh, sure! Will also work on the tests