Skip to content

Commit 8afab38

Browse files
authored
Merge 74d6f2e into 1df3d26
2 parents 1df3d26 + 74d6f2e commit 8afab38

17 files changed

+304
-23
lines changed

common/api-review/firestore.api.md

+6
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ export interface ExperimentalLongPollingOptions {
211211
timeoutSeconds?: number;
212212
}
213213

214+
// @public
215+
export interface ExperimentalOptions {
216+
sendWriteRequestsDelayMs?: number;
217+
}
218+
214219
// @public
215220
export class FieldPath {
216221
constructor(...fieldNames: string[]);
@@ -252,6 +257,7 @@ export type FirestoreLocalCache = MemoryLocalCache | PersistentLocalCache;
252257
// @public
253258
export interface FirestoreSettings {
254259
cacheSizeBytes?: number;
260+
experimental?: ExperimentalOptions;
255261
experimentalAutoDetectLongPolling?: boolean;
256262
experimentalForceLongPolling?: boolean;
257263
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;

docs-devsite/_toc.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ toc:
221221
path: /docs/reference/js/firestore_.documentsnapshot.md
222222
- title: ExperimentalLongPollingOptions
223223
path: /docs/reference/js/firestore_.experimentallongpollingoptions.md
224+
- title: ExperimentalOptions
225+
path: /docs/reference/js/firestore_.experimentaloptions.md
224226
- title: FieldPath
225227
path: /docs/reference/js/firestore_.fieldpath.md
226228
- title: FieldValue
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
Project: /docs/reference/js/_project.yaml
2+
Book: /docs/reference/_book.yaml
3+
page_type: reference
4+
5+
{% comment %}
6+
DO NOT EDIT THIS FILE!
7+
This is generated by the JS SDK team, and any local changes will be
8+
overwritten. Changes should be made in the source code at
9+
https://github.com/firebase/firebase-js-sdk
10+
{% endcomment %}
11+
12+
# ExperimentalOptions interface
13+
Experimental options to configure the Firestore SDK.
14+
15+
Note: This interface is "experimental" and is subject to change.
16+
17+
See `FirestoreSettings.experimental`<!-- -->.
18+
19+
<b>Signature:</b>
20+
21+
```typescript
22+
export declare interface ExperimentalOptions
23+
```
24+
25+
## Properties
26+
27+
| Property | Type | Description |
28+
| --- | --- | --- |
29+
| [sendWriteRequestsDelayMs](./firestore_.experimentaloptions.md#experimentaloptionssendwriterequestsdelayms) | number | The maximum amount of time, in milliseconds, to wait before sending a Firestore "write" request to the backend. If <code>undefined</code> then do not delay at all.<!-- -->A delay can be useful because it enables the in-memory "write pipeline" to gather together multiple write requests and send them in a single HTTP request to the backend, rather than one HTTP request per write request, as is done when by default, or when this property is <code>undefined</code>. Note that there is a hardcoded limit to the number of write requests that are sent at once, so setting a very large value for this property will not necessarily cause \_all\_ write requests to be sent in a single HTTP request; however, it \_could\_ greatly reduce the number of distinct HTTP requests that are used.<!-- -->The value must be an integer value strictly greater than zero and less than or equal to 10000 (10 seconds). A value of <code>200</code> is a good starting point to minimize write latency yet still enable some amount of batching.<!-- -->See https://github.com/firebase/firebase-js-sdk/issues/5971 for rationale and background information that motivated this option. |
30+
31+
## ExperimentalOptions.sendWriteRequestsDelayMs
32+
33+
The maximum amount of time, in milliseconds, to wait before sending a Firestore "write" request to the backend. If `undefined` then do not delay at all.
34+
35+
A delay can be useful because it enables the in-memory "write pipeline" to gather together multiple write requests and send them in a single HTTP request to the backend, rather than one HTTP request per write request, as is done when by default, or when this property is `undefined`<!-- -->. Note that there is a hardcoded limit to the number of write requests that are sent at once, so setting a very large value for this property will not necessarily cause \_all\_ write requests to be sent in a single HTTP request; however, it \_could\_ greatly reduce the number of distinct HTTP requests that are used.
36+
37+
The value must be an integer value strictly greater than zero and less than or equal to 10000 (10 seconds). A value of `200` is a good starting point to minimize write latency yet still enable some amount of batching.
38+
39+
See https://github.com/firebase/firebase-js-sdk/issues/5971 for rationale and background information that motivated this option.
40+
41+
<b>Signature:</b>
42+
43+
```typescript
44+
sendWriteRequestsDelayMs?: number;
45+
```

docs-devsite/firestore_.firestoresettings.md

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export declare interface FirestoreSettings
2323
| Property | Type | Description |
2424
| --- | --- | --- |
2525
| [cacheSizeBytes](./firestore_.firestoresettings.md#firestoresettingscachesizebytes) | number | NOTE: This field will be deprecated in a future major release. Use <code>cache</code> field instead to specify cache size, and other cache configurations.<!-- -->An approximate cache size threshold for the on-disk data. If the cache grows beyond this size, Firestore will start removing data that hasn't been recently used. The size is not a guarantee that the cache will stay below that size, only that if the cache exceeds the given size, cleanup will be attempted.<!-- -->The default value is 40 MB. The threshold must be set to at least 1 MB, and can be set to <code>CACHE_SIZE_UNLIMITED</code> to disable garbage collection. |
26+
| [experimental](./firestore_.firestoresettings.md#firestoresettingsexperimental) | [ExperimentalOptions](./firestore_.experimentaloptions.md#experimentaloptions_interface) | Options that are "experimental", meaning that their semantics are subject to change at any time without notice, up to and including complete removal. |
2627
| [experimentalAutoDetectLongPolling](./firestore_.firestoresettings.md#firestoresettingsexperimentalautodetectlongpolling) | boolean | Configures the SDK's underlying transport (WebChannel) to automatically detect if long-polling should be used. This is very similar to <code>experimentalForceLongPolling</code>, but only uses long-polling if required.<!-- -->After having had a default value of <code>false</code> since its inception in 2019, the default value of this setting was changed in May 2023 to <code>true</code> in v9.22.0 of the Firebase JavaScript SDK. That is, auto-detection of long polling is now enabled by default. To disable it, set this setting to <code>false</code>, and please open a GitHub issue to share the problems that motivated you disabling long-polling auto-detection.<!-- -->This setting cannot be used in a Node.js environment. |
2728
| [experimentalForceLongPolling](./firestore_.firestoresettings.md#firestoresettingsexperimentalforcelongpolling) | boolean | Forces the SDKs underlying network transport (WebChannel) to use long-polling. Each response from the backend will be closed immediately after the backend sends data (by default responses are kept open in case the backend has more data to send). This avoids incompatibility issues with certain proxies, antivirus software, etc. that incorrectly buffer traffic indefinitely. Use of this option will cause some performance degradation though.<!-- -->This setting cannot be used with <code>experimentalAutoDetectLongPolling</code> and may be removed in a future release. If you find yourself using it to work around a specific network reliability issue, please tell us about it in https://github.com/firebase/firebase-js-sdk/issues/1674.<!-- -->This setting cannot be used in a Node.js environment. |
2829
| [experimentalLongPollingOptions](./firestore_.firestoresettings.md#firestoresettingsexperimentallongpollingoptions) | [ExperimentalLongPollingOptions](./firestore_.experimentallongpollingoptions.md#experimentallongpollingoptions_interface) | Options that configure the SDKs underlying network transport (WebChannel) when long-polling is used.<!-- -->These options are only used if <code>experimentalForceLongPolling</code> is true or if <code>experimentalAutoDetectLongPolling</code> is true and the auto-detection determined that long-polling was needed. Otherwise, these options have no effect. |
@@ -45,6 +46,16 @@ The default value is 40 MB. The threshold must be set to at least 1 MB, and can
4546
cacheSizeBytes?: number;
4647
```
4748

49+
## FirestoreSettings.experimental
50+
51+
Options that are "experimental", meaning that their semantics are subject to change at any time without notice, up to and including complete removal.
52+
53+
<b>Signature:</b>
54+
55+
```typescript
56+
experimental?: ExperimentalOptions;
57+
```
58+
4859
## FirestoreSettings.experimentalAutoDetectLongPolling
4960

5061
Configures the SDK's underlying transport (WebChannel) to automatically detect if long-polling should be used. This is very similar to `experimentalForceLongPolling`<!-- -->, but only uses long-polling if required.

docs-devsite/firestore_.md

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ https://github.com/firebase/firebase-js-sdk
168168
| [DocumentChange](./firestore_.documentchange.md#documentchange_interface) | A <code>DocumentChange</code> represents a change to the documents matching a query. It contains the document affected and the type of change that occurred. |
169169
| [DocumentData](./firestore_.documentdata.md#documentdata_interface) | Document data (for use with [setDoc()](./firestore_lite.md#setdoc_ee215ad)<!-- -->) consists of fields mapped to values. |
170170
| [ExperimentalLongPollingOptions](./firestore_.experimentallongpollingoptions.md#experimentallongpollingoptions_interface) | Options that configure the SDK’s underlying network transport (WebChannel) when long-polling is used.<!-- -->Note: This interface is "experimental" and is subject to change.<!-- -->See <code>FirestoreSettings.experimentalAutoDetectLongPolling</code>, <code>FirestoreSettings.experimentalForceLongPolling</code>, and <code>FirestoreSettings.experimentalLongPollingOptions</code>. |
171+
| [ExperimentalOptions](./firestore_.experimentaloptions.md#experimentaloptions_interface) | Experimental options to configure the Firestore SDK.<!-- -->Note: This interface is "experimental" and is subject to change.<!-- -->See <code>FirestoreSettings.experimental</code>. |
171172
| [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface) | Converter used by <code>withConverter()</code> to transform user objects of type <code>AppModelType</code> into Firestore data of type <code>DbModelType</code>.<!-- -->Using the converter allows you to specify generic type arguments when storing and retrieving objects from Firestore.<!-- -->In this context, an "AppModel" is a class that is used in an application to package together related information and functionality. Such a class could, for example, have properties with complex, nested data types, properties used for memoization, properties of types not supported by Firestore (such as <code>symbol</code> and <code>bigint</code>), and helper functions that perform compound operations. Such classes are not suitable and/or possible to store into a Firestore database. Instead, instances of such classes need to be converted to "plain old JavaScript objects" (POJOs) with exclusively primitive properties, potentially nested inside other POJOs or arrays of POJOs. In this context, this type is referred to as the "DbModel" and would be an object suitable for persisting into Firestore. For convenience, applications can implement <code>FirestoreDataConverter</code> and register the converter with Firestore objects, such as <code>DocumentReference</code> or <code>Query</code>, to automatically convert <code>AppModel</code> to <code>DbModel</code> when storing into Firestore, and convert <code>DbModel</code> to <code>AppModel</code> when retrieving from Firestore. |
172173
| [FirestoreSettings](./firestore_.firestoresettings.md#firestoresettings_interface) | Specifies custom configurations for your Cloud Firestore instance. You must set these before invoking any other methods. |
173174
| [Index](./firestore_.index.md#index_interface) | <b><i>(Public Preview)</i></b> The SDK definition of a Firestore index. |

packages/firestore/src/api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export {
8484
export { FirestoreSettings, PersistenceSettings } from './api/settings';
8585
export type { PrivateSettings } from './lite-api/settings';
8686
export { ExperimentalLongPollingOptions } from './api/long_polling_options';
87+
export { ExperimentalOptions } from './api/experimental_options';
8788

8889
export {
8990
DocumentChange,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* Experimental options to configure the Firestore SDK.
20+
*
21+
* Note: This interface is "experimental" and is subject to change.
22+
*
23+
* See `FirestoreSettings.experimental`.
24+
*/
25+
export interface ExperimentalOptions {
26+
/**
27+
* The maximum amount of time, in milliseconds, to wait before sending a
28+
* Firestore "write" request to the backend. If `undefined` then do not delay
29+
* at all.
30+
*
31+
* A delay can be useful because it enables the in-memory "write pipeline" to
32+
* gather together multiple write requests and send them in a single HTTP
33+
* request to the backend, rather than one HTTP request per write request, as
34+
* is done when by default, or when this property is `undefined`. Note that
35+
* there is a hardcoded limit to the number of write requests that are sent at
36+
* once, so setting a very large value for this property will not necessarily
37+
* cause _all_ write requests to be sent in a single HTTP request; however, it
38+
* _could_ greatly reduce the number of distinct HTTP requests that are used.
39+
*
40+
* The value must be an integer value strictly greater than zero and less than
41+
* or equal to 10000 (10 seconds). A value of `200` is a good starting point
42+
* to minimize write latency yet still enable some amount of batching.
43+
*
44+
* See https://github.com/firebase/firebase-js-sdk/issues/5971 for rationale
45+
* and background information that motivated this option.
46+
*/
47+
sendWriteRequestsDelayMs?: number;
48+
}
49+
50+
/**
51+
* Compares two `ExperimentalOptions` objects for equality.
52+
*/
53+
export function experimentalOptionsEqual(
54+
options1: ExperimentalOptions,
55+
options2: ExperimentalOptions
56+
): boolean {
57+
return (
58+
options1.sendWriteRequestsDelayMs === options2.sendWriteRequestsDelayMs
59+
);
60+
}
61+
62+
/**
63+
* Creates and returns a new `ExperimentalOptions` with the same
64+
* option values as the given instance.
65+
*/
66+
export function cloneExperimentalOptions(
67+
options: ExperimentalOptions
68+
): ExperimentalOptions {
69+
const clone: ExperimentalOptions = {};
70+
71+
if (options.sendWriteRequestsDelayMs !== undefined) {
72+
clone.sendWriteRequestsDelayMs = options.sendWriteRequestsDelayMs;
73+
}
74+
75+
return clone;
76+
}

packages/firestore/src/api/settings.ts

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import { FirestoreSettings as LiteSettings } from '../lite-api/settings';
1919

2020
import { FirestoreLocalCache } from './cache_config';
21+
import { ExperimentalOptions } from './experimental_options';
2122
import { ExperimentalLongPollingOptions } from './long_polling_options';
2223

2324
export { DEFAULT_HOST } from '../lite-api/settings';
@@ -114,4 +115,10 @@ export interface FirestoreSettings extends LiteSettings {
114115
* effect.
115116
*/
116117
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
118+
119+
/**
120+
* Options that are "experimental", meaning that their semantics are subject
121+
* to change at any time without notice, up to and including complete removal.
122+
*/
123+
experimental?: ExperimentalOptions;
117124
}

packages/firestore/src/core/component_provider.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,8 @@ export class OnlineComponentProvider {
485485
onlineState,
486486
OnlineStateSource.RemoteStore
487487
),
488-
newConnectivityMonitor()
488+
newConnectivityMonitor(),
489+
cfg.databaseInfo.sendWriteRequestsDelayMs
489490
);
490491
}
491492

packages/firestore/src/core/database_info.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export class DatabaseInfo {
3838
* @param longPollingOptions Options that configure long-polling.
3939
* @param useFetchStreams Whether to use the Fetch API instead of
4040
* XMLHTTPRequest
41+
* @param sendWriteRequestsDelayMs The delay, in milliseconds, to use before
42+
* sending write requests over the wire in remote store.
4143
*/
4244
constructor(
4345
readonly databaseId: DatabaseId,
@@ -48,7 +50,8 @@ export class DatabaseInfo {
4850
readonly forceLongPolling: boolean,
4951
readonly autoDetectLongPolling: boolean,
5052
readonly longPollingOptions: ExperimentalLongPollingOptions,
51-
readonly useFetchStreams: boolean
53+
readonly useFetchStreams: boolean,
54+
readonly sendWriteRequestsDelayMs: number | null
5255
) {}
5356
}
5457

packages/firestore/src/lite-api/components.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export function makeDatabaseInfo(
119119
settings.experimentalForceLongPolling,
120120
settings.experimentalAutoDetectLongPolling,
121121
cloneLongPollingOptions(settings.experimentalLongPollingOptions),
122-
settings.useFetchStreams
122+
settings.useFetchStreams,
123+
settings.experimental.sendWriteRequestsDelayMs ?? null
123124
);
124125
}

packages/firestore/src/lite-api/settings.ts

+39-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ import { EmulatorMockTokenOptions } from '@firebase/util';
1919

2020
import { FirestoreLocalCache } from '../api/cache_config';
2121
import { CredentialsSettings } from '../api/credentials';
22+
import {
23+
ExperimentalOptions,
24+
cloneExperimentalOptions,
25+
experimentalOptionsEqual
26+
} from '../api/experimental_options';
2227
import {
2328
ExperimentalLongPollingOptions,
2429
cloneLongPollingOptions,
@@ -50,6 +55,10 @@ const MAX_LONG_POLLING_TIMEOUT_SECONDS = 30;
5055
// Whether long-polling auto-detected is enabled by default.
5156
const DEFAULT_AUTO_DETECT_LONG_POLLING = true;
5257

58+
// Set some maximum value for `sendWriteRequestsDelayMs` to avoid it being set
59+
// to a value so large that it appears that write requests are never being sent.
60+
const MAX_SEND_WRITE_REQUEST_DELAY_MS = 10000;
61+
5362
/**
5463
* Specifies custom configurations for your Cloud Firestore instance.
5564
* You must set these before invoking any other methods.
@@ -83,6 +92,7 @@ export interface PrivateSettings extends FirestoreSettings {
8392
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
8493
useFetchStreams?: boolean;
8594
emulatorOptions?: { mockUserToken?: EmulatorMockTokenOptions | string };
95+
experimental?: ExperimentalOptions;
8696

8797
localCache?: FirestoreLocalCache;
8898
}
@@ -111,6 +121,7 @@ export class FirestoreSettingsImpl {
111121

112122
readonly useFetchStreams: boolean;
113123
readonly localCache?: FirestoreLocalCache;
124+
readonly experimental: ExperimentalOptions;
114125

115126
// Can be a google-auth-library or gapi client.
116127
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -178,6 +189,9 @@ export class FirestoreSettingsImpl {
178189
validateLongPollingOptions(this.experimentalLongPollingOptions);
179190

180191
this.useFetchStreams = !!settings.useFetchStreams;
192+
193+
this.experimental = cloneExperimentalOptions(settings.experimental ?? {});
194+
validateExperimentalOptions(this.experimental);
181195
}
182196

183197
isEqual(other: FirestoreSettingsImpl): boolean {
@@ -195,7 +209,8 @@ export class FirestoreSettingsImpl {
195209
other.experimentalLongPollingOptions
196210
) &&
197211
this.ignoreUndefinedProperties === other.ignoreUndefinedProperties &&
198-
this.useFetchStreams === other.useFetchStreams
212+
this.useFetchStreams === other.useFetchStreams &&
213+
experimentalOptionsEqual(this.experimental, other.experimental)
199214
);
200215
}
201216
}
@@ -227,3 +242,26 @@ function validateLongPollingOptions(
227242
}
228243
}
229244
}
245+
246+
function validateExperimentalOptions(options: ExperimentalOptions): void {
247+
if (options.sendWriteRequestsDelayMs !== undefined) {
248+
if (isNaN(options.sendWriteRequestsDelayMs)) {
249+
throw new FirestoreError(
250+
Code.INVALID_ARGUMENT,
251+
`invalid sendWriteRequestsDelayMs: ` +
252+
`${options.sendWriteRequestsDelayMs} (must not be NaN)`
253+
);
254+
}
255+
if (
256+
options.sendWriteRequestsDelayMs <= 0 ||
257+
options.sendWriteRequestsDelayMs > MAX_SEND_WRITE_REQUEST_DELAY_MS
258+
) {
259+
throw new FirestoreError(
260+
Code.INVALID_ARGUMENT,
261+
`invalid sendWriteRequestsDelayMs: ` +
262+
`${options.sendWriteRequestsDelayMs} (must be greater than zero ` +
263+
`and less than or equal to ${MAX_SEND_WRITE_REQUEST_DELAY_MS})`
264+
);
265+
}
266+
}
267+
}

0 commit comments

Comments
 (0)