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

Add option to allow SDK create cache indexes automatically to improve query execution locally #4987

Merged
merged 28 commits into from
Jul 26, 2023

Conversation

cherylEnkidu
Copy link
Contributor

@cherylEnkidu cherylEnkidu commented May 8, 2023

Preparing work for index auto creation feature.

This PR was ported to the web sdk in firebase/firebase-js-sdk#7542.

@github-actions
Copy link
Contributor

github-actions bot commented May 8, 2023

Javadoc Changes:
--- /Users/runner/diff/original/firebase-kotlindoc/android/com/google/firebase/firestore/FirebaseFirestore.html	2023-07-26 17:30:50.000000000 +0000
+++ /Users/runner/diff/modified/firebase-kotlindoc/android/com/google/firebase/firestore/FirebaseFirestore.html	2023-07-26 17:24:33.000000000 +0000
@@ -22,6 +22,27 @@
         </colgroup>
         <thead>
           <tr>
+            <th colspan="100%"><h3>Public fields</h3></th>
+          </tr>
+        </thead>
+        <tbody class="list">
+          <tr>
+            <td><code>@<a href="https://developer.android.com/reference/kotlin/androidx/annotation/Nullable.html">Nullable</a> <a href="/docs/reference/android/com/google/firebase/firestore/PersistentCacheIndexManager.html">PersistentCacheIndexManager</a></code></td>
+            <td>
+              <div><code><a href="/docs/reference/android/com/google/firebase/firestore/FirebaseFirestore.html#persistentCacheIndexManager()">persistentCacheIndexManager</a></code></div>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div class="devsite-table-wrapper">
+      <table class="responsive">
+        <colgroup>
+          <col width="40%">
+          <col>
+        </colgroup>
+        <thead>
+          <tr>
             <th colspan="100%"><h3>Public methods</h3></th>
           </tr>
         </thead>
@@ -233,6 +254,13 @@
       </table>
     </div>
     <div class="list">
+      <h2>Public fields</h2>
+      <div class="api-item"><a name="getPersistentCacheIndexManager()"></a><a name="setPersistentCacheIndexManager()"></a><a name="getPersistentCacheIndexManager--"></a><a name="setPersistentCacheIndexManager--"></a>
+        <h3 class="api-name" id="persistentCacheIndexManager()">persistentCacheIndexManager</h3>
+        <pre class="api-signature no-pretty-print">public&nbsp;@<a href="https://developer.android.com/reference/kotlin/androidx/annotation/Nullable.html">Nullable</a> <a href="/docs/reference/android/com/google/firebase/firestore/PersistentCacheIndexManager.html">PersistentCacheIndexManager</a>&nbsp;<a href="/docs/reference/android/com/google/firebase/firestore/FirebaseFirestore.html#persistentCacheIndexManager()">persistentCacheIndexManager</a></pre>
+      </div>
+    </div>
+    <div class="list">
       <h2>Public methods</h2>
       <div class="api-item"><a name="addSnapshotsInSyncListener-java.lang.Runnable-"></a><a name="addsnapshotsinsynclistener"></a>
         <h3 class="api-name" id="addSnapshotsInSyncListener(java.lang.Runnable)">addSnapshotsInSyncListener</h3>
--- /Users/runner/diff/original/firebase-kotlindoc/kotlin/com/google/firebase/firestore/FirebaseFirestore.html	2023-07-26 17:30:50.000000000 +0000
+++ /Users/runner/diff/modified/firebase-kotlindoc/kotlin/com/google/firebase/firestore/FirebaseFirestore.html	2023-07-26 17:24:32.000000000 +0000
@@ -232,6 +232,27 @@
         </tbody>
       </table>
     </div>
+    <div class="devsite-table-wrapper">
+      <table class="responsive">
+        <colgroup>
+          <col width="40%">
+          <col>
+        </colgroup>
+        <thead>
+          <tr>
+            <th colspan="100%"><h3>Public properties</h3></th>
+          </tr>
+        </thead>
+        <tbody class="list">
+          <tr>
+            <td><code><a href="/docs/reference/kotlin/com/google/firebase/firestore/PersistentCacheIndexManager.html">PersistentCacheIndexManager</a>?</code></td>
+            <td>
+              <div><code><a href="/docs/reference/kotlin/com/google/firebase/firestore/FirebaseFirestore.html#persistentCacheIndexManager()">persistentCacheIndexManager</a></code></div>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
     <div class="list">
       <h2>Public functions</h2>
       <div class="api-item"><a name="addSnapshotsInSyncListener-java.lang.Runnable-"></a><a name="addsnapshotsinsynclistener"></a>
@@ -1211,6 +1232,13 @@
         </div>
       </div>
     </div>
+    <div class="list">
+      <h2>Public properties</h2>
+      <div class="api-item"><a name="getPersistentCacheIndexManager()"></a><a name="setPersistentCacheIndexManager()"></a><a name="getPersistentCacheIndexManager--"></a><a name="setPersistentCacheIndexManager--"></a>
+        <h3 class="api-name" id="persistentCacheIndexManager()">persistentCacheIndexManager</h3>
+        <pre class="api-signature no-pretty-print">val&nbsp;<a href="/docs/reference/kotlin/com/google/firebase/firestore/FirebaseFirestore.html#persistentCacheIndexManager()">persistentCacheIndexManager</a>:&nbsp;<a href="/docs/reference/kotlin/com/google/firebase/firestore/PersistentCacheIndexManager.html">PersistentCacheIndexManager</a>?</pre>
+      </div>
+    </div>
   </body>
 </html>
 

@google-oss-bot
Copy link
Contributor

google-oss-bot commented May 8, 2023

Coverage Report 1

Affected Products

  • firebase-firestore

    Overall coverage changed from 44.35% (fc5cde4) to 44.47% (8093125) by +0.12%.

    13 individual files with coverage change

    FilenameBase (fc5cde4)Merge (8093125)Diff
    AutoValue_FieldIndex_IndexOffset.java58.62%55.17%-3.45%
    FirebaseFirestore.java38.25%37.04%-1.21%
    FirestoreClient.java36.11%35.37%-0.74%
    LruGarbageCollector.java97.27%93.64%-3.64%
    MemoryIndexManager.java68.97%66.67%-2.30%
    MemoryRemoteDocumentCache.java98.28%98.31%+0.03%
    PatchMutation.java100.00%98.39%-1.61%
    PersistentCacheIndexManager.java?0.00%?
    QueryContext.java?100.00%?
    QueryEngine.java98.63%99.02%+0.39%
    SQLiteIndexManager.java99.49%99.50%+0.01%
    SQLiteRemoteDocumentCache.java98.21%98.31%+0.09%
    TargetIndexMatcher.java100.00%97.78%-2.22%

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/thMDSMFTXj.html

@github-actions
Copy link
Contributor

github-actions bot commented May 8, 2023

Unit Test Results

   162 files  +   108     162 suites  +108   2m 44s ⏱️ - 4m 21s
1 188 tests +   718  1 172 ✔️ +   702  16 💤 +16  0 ±0 
2 376 runs  +1 436  2 344 ✔️ +1 404  32 💤 +32  0 ±0 

Results for commit fb12477. ± Comparison against base commit 4d1cc39.

This pull request removes 470 and adds 1188 tests. Note that renamed tests count towards both.
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testBindsService_oAndTargetingO
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testNoWrappedIntent
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testNullIntent
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_OTargetingO_highPriority
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_fallsBackToBindService
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_notOButTargetingO
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_notOButTargetingO[19]
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_notOButTargetingO[21]
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_notOButTargetingO[22]
com.google.firebase.iid.FirebaseInstanceIdWithFcmReceiverRoboTest ‑ testStartsService_notOButTargetingO[23]
…
com.google.firebase.TimestampTest ‑ testCompare
com.google.firebase.TimestampTest ‑ testFromDate
com.google.firebase.TimestampTest ‑ testRejectBadDates
com.google.firebase.TimestampTest ‑ testTimestampParcelable
com.google.firebase.firestore.AggregateQuerySnapshotTest ‑ createWithCountShouldReturnInstanceWithTheGivenQueryAndCount
com.google.firebase.firestore.AggregateQueryTest ‑ testSourceMustNotBeNull
com.google.firebase.firestore.BlobTest ‑ testComparison
com.google.firebase.firestore.BlobTest ‑ testEquals
com.google.firebase.firestore.BlobTest ‑ testMutableBytes
com.google.firebase.firestore.CollectionReferenceTest ‑ testEquals
…

♻️ This comment has been updated with latest results.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented May 8, 2023

Size Report 1

Affected Products

  • firebase-firestore

    TypeBase (fc5cde4)Merge (8093125)Diff
    aar1.36 MB1.36 MB+3.69 kB (+0.3%)
    apk (release)3.95 MB3.95 MB+2.24 kB (+0.1%)

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/bGsGrjWIm8.html

@google-oss-bot
Copy link
Contributor

google-oss-bot commented May 8, 2023

Startup Time Report 1

Note: Layout is sometimes suboptimal due to limited formatting support on GitHub. Please check this report on GCS.

Notes

Startup Times

  • fire-fst

    DeviceStatisticsDistributions
    oriole-32
    Percentilefc5cde48093125DiffSignificant (?)
    p10324 ±18 μs316 ±12 μs-7.82 μs (-2.4%)NO
    p25341 ±23 μs327 ±15 μs-13.9 μs (-4.1%)NO
    p50370 ±41 μs345 ±24 μs-25.6 μs (-6.9%)NO
    p75436 ±73 μs403 ±74 μs-33.0 μs (-7.6%)NO
    p90523 ±126 μs479 ±95 μs-44.9 μs (-8.6%)NO

    20 test runs in comparison
    CommitTest Runs
    fc5cde4
    • 2023-07-26_15:12:33.900360_wSfj
    • 2023-07-26_15:12:33.905569_ojoz
    • 2023-07-26_15:12:33.905583_UFLj
    • 2023-07-26_15:12:33.905590_Tuns
    • 2023-07-26_15:12:33.905596_YiIP
    • 2023-07-26_15:12:33.905603_LwJo
    • 2023-07-26_15:12:33.905610_GOAh
    • 2023-07-26_15:12:33.905616_TVTP
    • 2023-07-26_15:12:33.905621_AXaX
    • 2023-07-26_15:12:33.905630_nbIy
    8093125
    • 2023-07-26_17:36:22.067399_WwtC
    • 2023-07-26_17:36:22.072489_QqzG
    • 2023-07-26_17:36:22.072538_JevK
    • 2023-07-26_17:36:22.072558_mvSw
    • 2023-07-26_17:36:22.072575_Jzfo
    • 2023-07-26_17:36:22.072591_Ipnq
    • 2023-07-26_17:36:22.072608_TzMg
    • 2023-07-26_17:36:22.072622_tRtH
    • 2023-07-26_17:36:22.072637_jsIq
    • 2023-07-26_17:36:22.072659_OlMe
    redfin-30
    Percentilefc5cde48093125DiffSignificant (?)
    p10614 ±36 μs588 ±16 μs-25.9 μs (-4.2%)NO
    p25633 ±42 μs601 ±18 μs-31.9 μs (-5.0%)NO
    p50661 ±54 μs620 ±24 μs-41.3 μs (-6.2%)NO
    p75693 ±66 μs644 ±32 μs-49.2 μs (-7.1%)NO
    p90732 ±83 μs682 ±45 μs-49.6 μs (-6.8%)NO

    20 test runs in comparison
    CommitTest Runs
    fc5cde4
    • 2023-07-26_15:12:33.900360_wSfj
    • 2023-07-26_15:12:33.905569_ojoz
    • 2023-07-26_15:12:33.905583_UFLj
    • 2023-07-26_15:12:33.905590_Tuns
    • 2023-07-26_15:12:33.905596_YiIP
    • 2023-07-26_15:12:33.905603_LwJo
    • 2023-07-26_15:12:33.905610_GOAh
    • 2023-07-26_15:12:33.905616_TVTP
    • 2023-07-26_15:12:33.905621_AXaX
    • 2023-07-26_15:12:33.905630_nbIy
    8093125
    • 2023-07-26_17:36:22.067399_WwtC
    • 2023-07-26_17:36:22.072489_QqzG
    • 2023-07-26_17:36:22.072538_JevK
    • 2023-07-26_17:36:22.072558_mvSw
    • 2023-07-26_17:36:22.072575_Jzfo
    • 2023-07-26_17:36:22.072591_Ipnq
    • 2023-07-26_17:36:22.072608_TzMg
    • 2023-07-26_17:36:22.072622_tRtH
    • 2023-07-26_17:36:22.072637_jsIq
    • 2023-07-26_17:36:22.072659_OlMe
  • timeToInitialDisplay

    DeviceStatisticsDistributions
    oriole-32
    Percentilefc5cde48093125DiffSignificant (?)
    p10201 ±5 ms203 ±2 ms+1.94 ms (+1.0%)NO
    p25207 ±5 ms210 ±2 ms+2.63 ms (+1.3%)NO
    p50214 ±6 ms218 ±2 ms+3.27 ms (+1.5%)NO
    p75222 ±6 ms227 ±4 ms+4.54 ms (+2.0%)NO
    p90230 ±7 ms239 ±5 ms+8.79 ms (+3.8%)NO

    20 test runs in comparison
    CommitTest Runs
    fc5cde4
    • 2023-07-26_15:12:33.900360_wSfj
    • 2023-07-26_15:12:33.905569_ojoz
    • 2023-07-26_15:12:33.905583_UFLj
    • 2023-07-26_15:12:33.905590_Tuns
    • 2023-07-26_15:12:33.905596_YiIP
    • 2023-07-26_15:12:33.905603_LwJo
    • 2023-07-26_15:12:33.905610_GOAh
    • 2023-07-26_15:12:33.905616_TVTP
    • 2023-07-26_15:12:33.905621_AXaX
    • 2023-07-26_15:12:33.905630_nbIy
    8093125
    • 2023-07-26_17:36:22.067399_WwtC
    • 2023-07-26_17:36:22.072489_QqzG
    • 2023-07-26_17:36:22.072538_JevK
    • 2023-07-26_17:36:22.072558_mvSw
    • 2023-07-26_17:36:22.072575_Jzfo
    • 2023-07-26_17:36:22.072591_Ipnq
    • 2023-07-26_17:36:22.072608_TzMg
    • 2023-07-26_17:36:22.072622_tRtH
    • 2023-07-26_17:36:22.072637_jsIq
    • 2023-07-26_17:36:22.072659_OlMe
    redfin-30
    Percentilefc5cde48093125DiffSignificant (?)
    p10245 ±4 ms262 ±7 ms+17.6 ms (+7.2%)NO
    p25252 ±5 ms268 ±6 ms+15.8 ms (+6.3%)NO
    p50259 ±5 ms275 ±6 ms+16.4 ms (+6.3%)NO
    p75267 ±5 ms286 ±6 ms+19.2 ms (+7.2%)MAYBE
    p90278 ±5 ms301 ±6 ms+23.0 ms (+8.3%)MAYBE

    20 test runs in comparison
    CommitTest Runs
    fc5cde4
    • 2023-07-26_15:12:33.900360_wSfj
    • 2023-07-26_15:12:33.905569_ojoz
    • 2023-07-26_15:12:33.905583_UFLj
    • 2023-07-26_15:12:33.905590_Tuns
    • 2023-07-26_15:12:33.905596_YiIP
    • 2023-07-26_15:12:33.905603_LwJo
    • 2023-07-26_15:12:33.905610_GOAh
    • 2023-07-26_15:12:33.905616_TVTP
    • 2023-07-26_15:12:33.905621_AXaX
    • 2023-07-26_15:12:33.905630_nbIy
    8093125
    • 2023-07-26_17:36:22.067399_WwtC
    • 2023-07-26_17:36:22.072489_QqzG
    • 2023-07-26_17:36:22.072538_JevK
    • 2023-07-26_17:36:22.072558_mvSw
    • 2023-07-26_17:36:22.072575_Jzfo
    • 2023-07-26_17:36:22.072591_Ipnq
    • 2023-07-26_17:36:22.072608_TzMg
    • 2023-07-26_17:36:22.072622_tRtH
    • 2023-07-26_17:36:22.072637_jsIq
    • 2023-07-26_17:36:22.072659_OlMe

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/tczw3eSsBV/index.html

Copy link
Contributor

@wu-hui wu-hui left a comment

Choose a reason for hiding this comment

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

Some comments, I think you get the overall idea however.

While you are at it, maybe write a simple count-based heuristics: compare the doc reads from full collection scan, and indexed-doc reads from a imaginary perfect indexed query, and make a decision to turn on indexing?

@google-oss-bot
Copy link
Contributor

The public api surface has changed for the subproject firebase-firestore:
error: Added method com.google.firebase.firestore.PersistentCacheSettings.autoClientIndexingEnabled() [AddedMethod]
error: Added method com.google.firebase.firestore.PersistentCacheSettings.Builder.enableAutoClientIndexing(boolean) [AddedMethod]

Please update the api.txt files for the subprojects being affected by this change by running ./gradlew ${subproject}:generateApiTxtFile. Also perform a major/minor bump accordingly.

Copy link
Contributor

@wu-hui wu-hui left a comment

Choose a reason for hiding this comment

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

Looks good for a prototype.

I want to see some tests for the change in TargetIndexMatcher, then we should be able to move forward.

@google-oss-bot
Copy link
Contributor

The public api surface has changed for the subproject firebase-firestore:
error: Added method com.google.firebase.firestore.PersistentCacheSettings.autoClientIndexingEnabled() [AddedMethod]
error: Added method com.google.firebase.firestore.PersistentCacheSettings.Builder.enableAutoClientIndexing(boolean) [AddedMethod]

Please update the api.txt files for the subprojects being affected by this change by running ./gradlew ${subproject}:generateApiTxtFile. Also perform a major/minor bump accordingly.

1 similar comment
@google-oss-bot
Copy link
Contributor

The public api surface has changed for the subproject firebase-firestore:
error: Added method com.google.firebase.firestore.PersistentCacheSettings.autoClientIndexingEnabled() [AddedMethod]
error: Added method com.google.firebase.firestore.PersistentCacheSettings.Builder.enableAutoClientIndexing(boolean) [AddedMethod]

Please update the api.txt files for the subprojects being affected by this change by running ./gradlew ${subproject}:generateApiTxtFile. Also perform a major/minor bump accordingly.

@cherylEnkidu cherylEnkidu force-pushed the cheryllin/autoindexing branch 2 times, most recently from e5b8859 to 1e25c19 Compare June 27, 2023 03:28
@firebase firebase deleted a comment from google-oss-bot Jun 27, 2023
@firebase firebase deleted a comment from google-oss-bot Jun 27, 2023
@firebase firebase deleted a comment from google-oss-bot Jun 27, 2023
@firebase firebase deleted a comment from google-oss-bot Jun 27, 2023
@google-oss-bot
Copy link
Contributor

The public api surface has changed for the subproject firebase-firestore:
error: Added method com.google.firebase.firestore.FirebaseFirestore.getPersistentCacheIndexManager() [AddedMethod]

Please update the api.txt files for the subprojects being affected by this change by running ./gradlew ${subproject}:generateApiTxtFile. Also perform a major/minor bump accordingly.

2 similar comments
@google-oss-bot
Copy link
Contributor

The public api surface has changed for the subproject firebase-firestore:
error: Added method com.google.firebase.firestore.FirebaseFirestore.getPersistentCacheIndexManager() [AddedMethod]

Please update the api.txt files for the subprojects being affected by this change by running ./gradlew ${subproject}:generateApiTxtFile. Also perform a major/minor bump accordingly.

@google-oss-bot
Copy link
Contributor

The public api surface has changed for the subproject firebase-firestore:
error: Added method com.google.firebase.firestore.FirebaseFirestore.getPersistentCacheIndexManager() [AddedMethod]

Please update the api.txt files for the subprojects being affected by this change by running ./gradlew ${subproject}:generateApiTxtFile. Also perform a major/minor bump accordingly.

@cherylEnkidu cherylEnkidu force-pushed the cheryllin/autoindexing branch 3 times, most recently from d48504b to ad7fc61 Compare June 27, 2023 15:23
Copy link
Contributor

@wu-hui wu-hui left a comment

Choose a reason for hiding this comment

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

Please add more tests to SQLiteLocalStoreTest.

This is a place where you can:

  • setup a collection
  • turn on auto-indexing
  • run a query that should have added a field index
  • verify field indexes are added
  • call backfillIndexes()
  • verify query result and document read count is as expected as an indexed query.

You should be able to find enough examples in that file.

Copy link
Contributor

@wu-hui wu-hui left a comment

Choose a reason for hiding this comment

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

Some improvements are still needed, but I think we are very close now.

@wu-hui wu-hui assigned cherylEnkidu and unassigned wu-hui Jul 20, 2023
@cherylEnkidu cherylEnkidu assigned wu-hui and dconeybe and unassigned cherylEnkidu Jul 24, 2023
Copy link
Contributor

@wu-hui wu-hui left a comment

Choose a reason for hiding this comment

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

LGTM with some nits.

Copy link
Contributor

@dconeybe dconeybe left a comment

Choose a reason for hiding this comment

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

Thank you for addressing my previous comments. After re-reading, I have some more suggestions, mostly relating to readability rather than to functionality of the code.

Copy link
Contributor

@dconeybe dconeybe left a comment

Choose a reason for hiding this comment

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

Just a few final comments, but LGTM for the most part.

@cherylEnkidu cherylEnkidu assigned dconeybe and unassigned wu-hui and cherylEnkidu Jul 26, 2023
@cherylEnkidu cherylEnkidu enabled auto-merge (squash) July 26, 2023 22:08
@cherylEnkidu cherylEnkidu merged commit a5e7062 into master Jul 26, 2023
@cherylEnkidu cherylEnkidu deleted the cheryllin/autoindexing branch July 26, 2023 22:12
davidmotson pushed a commit that referenced this pull request Aug 3, 2023
… query execution locally (#4987)

* Add counter

* address feedback 1

* add copyright

* fix concurrency bug

* implement autoClientIndexing

* Add tests and fix bugs for BuildTargetIndex

* hide getter from public API

* move the flag from IndexManager to QueryEngine

* Address feedback

* move auto index flag to runtime

* Support old way to enable persistent for PersistentCacheManager

* Polish Tests

* Add hide and copyright

* clean up unused function

* Rename PersistentCacheManager to PersistentCacheIndexManager

* Remove unused QueryContext

* Address feedbacks other than adding tests and comments

* Change the api to match the update

* Add tests

* Increase tests coverage

* Add comments

* add configurable min documents to create indexes

* Address Denver's feedback

* Address feedback

* address more feedbacks

* use the number getting from 100 ~ 1000 documents experiment

* Address feedbacks

* improve debug log
@firebase firebase locked and limited conversation to collaborators Aug 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants