Skip to content

Commit 057eafd

Browse files
authored
Merge 3bd125d into 5a2ceb0
2 parents 5a2ceb0 + 3bd125d commit 057eafd

21 files changed

+481
-47
lines changed

.changeset/witty-wasps-play.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/firestore': minor
3+
'firebase': minor
4+
---
5+
6+
Added the ability to configure the long-polling hanging get request timeout using the new `idleHttpRequestTimeoutSeconds` setting

common/api-review/firestore.api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ export function endBefore(snapshot: DocumentSnapshot<unknown>): QueryEndAtConstr
184184
// @public
185185
export function endBefore(...fieldValues: unknown[]): QueryEndAtConstraint;
186186

187+
// @public
188+
export interface ExperimentalLongPollingOptions {
189+
timeoutSeconds?: number;
190+
}
191+
187192
// @public
188193
export class FieldPath {
189194
constructor(...fieldNames: string[]);
@@ -227,6 +232,7 @@ export interface FirestoreSettings {
227232
cacheSizeBytes?: number;
228233
experimentalAutoDetectLongPolling?: boolean;
229234
experimentalForceLongPolling?: boolean;
235+
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
230236
host?: string;
231237
ignoreUndefinedProperties?: boolean;
232238
localCache?: FirestoreLocalCache;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
# ExperimentalLongPollingOptions interface
13+
Options that configure the SDK’s underlying network transport (WebChannel) when long-polling is used.
14+
15+
Note: This interface is "experimental" and is subject to change.
16+
17+
See `FirestoreSettings.experimentalAutoDetectLongPolling`<!-- -->, `FirestoreSettings.experimentalForceLongPolling`<!-- -->, and `FirestoreSettings.experimentalLongPollingOptions`<!-- -->.
18+
19+
<b>Signature:</b>
20+
21+
```typescript
22+
export declare interface ExperimentalLongPollingOptions
23+
```
24+
25+
## Properties
26+
27+
| Property | Type | Description |
28+
| --- | --- | --- |
29+
| [timeoutSeconds](./firestore_.experimentallongpollingoptions.md#experimentallongpollingoptionstimeoutseconds) | number | The desired maximum timeout interval, in seconds, to complete a long-polling GET response. Valid values are between 5 and 30, inclusive. Floating point values are allowed and will be rounded to the nearest millisecond.<!-- -->By default, when long-polling is used the "hanging GET" request sent by the client times out after 30 seconds. To request a different timeout from the server, set this setting with the desired timeout.<!-- -->Changing the default timeout may be useful, for example, if the buffering proxy that necessitated enabling long-polling in the first place has a shorter timeout for hanging GET requests, in which case setting the long-polling timeout to a shorter value, such as 25 seconds, may fix prematurely-closed hanging GET requests. For example, see https://github.com/firebase/firebase-js-sdk/issues/6987. |
30+
31+
## ExperimentalLongPollingOptions.timeoutSeconds
32+
33+
The desired maximum timeout interval, in seconds, to complete a long-polling GET response. Valid values are between 5 and 30, inclusive. Floating point values are allowed and will be rounded to the nearest millisecond.
34+
35+
By default, when long-polling is used the "hanging GET" request sent by the client times out after 30 seconds. To request a different timeout from the server, set this setting with the desired timeout.
36+
37+
Changing the default timeout may be useful, for example, if the buffering proxy that necessitated enabling long-polling in the first place has a shorter timeout for hanging GET requests, in which case setting the long-polling timeout to a shorter value, such as 25 seconds, may fix prematurely-closed hanging GET requests. For example, see https://github.com/firebase/firebase-js-sdk/issues/6987.
38+
39+
<b>Signature:</b>
40+
41+
```typescript
42+
timeoutSeconds?: number;
43+
```

docs-devsite/firestore_.firestoresettings.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export declare interface FirestoreSettings
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. |
2626
| [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.<!-- -->This setting will likely be enabled by default in future releases and cannot be combined with <code>experimentalForceLongPolling</code>. |
2727
| [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. |
28+
| [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. |
2829
| [host](./firestore_.firestoresettings.md#firestoresettingshost) | string | The hostname to connect to. |
2930
| [ignoreUndefinedProperties](./firestore_.firestoresettings.md#firestoresettingsignoreundefinedproperties) | boolean | Whether to skip nested properties that are set to <code>undefined</code> during object serialization. If set to <code>true</code>, these properties are skipped and not written to Firestore. If set to <code>false</code> or omitted, the SDK throws an exception when it encounters properties of type <code>undefined</code>. |
3031
| [localCache](./firestore_.firestoresettings.md#firestoresettingslocalcache) | [FirestoreLocalCache](./firestore_.md#firestorelocalcache) | Specifies the cache used by the SDK. Available options are <code>MemoryLocalCache</code> and <code>IndexedDbLocalCache</code>, each with different configuration options.<!-- -->When unspecified, <code>MemoryLocalCache</code> will be used by default.<!-- -->NOTE: setting this field and <code>cacheSizeBytes</code> at the same time will throw exception during SDK initialization. Instead, using the configuration in the <code>FirestoreLocalCache</code> object to specify the cache size. |
@@ -68,6 +69,18 @@ This setting cannot be used with `experimentalAutoDetectLongPolling` and may be
6869
experimentalForceLongPolling?: boolean;
6970
```
7071

72+
## FirestoreSettings.experimentalLongPollingOptions
73+
74+
Options that configure the SDKs underlying network transport (WebChannel) when long-polling is used.
75+
76+
These options are only used if `experimentalForceLongPolling` is true or if `experimentalAutoDetectLongPolling` is true and the auto-detection determined that long-polling was needed. Otherwise, these options have no effect.
77+
78+
<b>Signature:</b>
79+
80+
```typescript
81+
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
82+
```
83+
7184
## FirestoreSettings.host
7285

7386
The hostname to connect to.

docs-devsite/firestore_.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ https://github.com/firebase/firebase-js-sdk
149149
| [AggregateSpec](./firestore_.aggregatespec.md#aggregatespec_interface) | Specifies a set of aggregations and their aliases. |
150150
| [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. |
151151
| [DocumentData](./firestore_.documentdata.md#documentdata_interface) | Document data (for use with [setDoc()](./firestore_lite.md#setdoc)<!-- -->) consists of fields mapped to values. |
152+
| [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>. |
152153
| [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface) | Converter used by <code>withConverter()</code> to transform user objects of type <code>T</code> into Firestore data.<!-- -->Using the converter allows you to specify generic type arguments when storing and retrieving objects from Firestore. |
153154
| [FirestoreSettings](./firestore_.firestoresettings.md#firestoresettings_interface) | Specifies custom configurations for your Cloud Firestore instance. You must set these before invoking any other methods. |
154155
| [Index](./firestore_.index.md#index_interface) | <b><i>(BETA)</i></b> The SDK definition of a Firestore index. |

packages/firestore/src/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export {
8282
} from './api/bundle';
8383

8484
export { FirestoreSettings, PersistenceSettings } from './api/settings';
85+
export { ExperimentalLongPollingOptions } from './api/long_polling_options';
8586

8687
export {
8788
DocumentChange,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
* Options that configure the SDK’s underlying network transport (WebChannel)
20+
* when long-polling is used.
21+
*
22+
* Note: This interface is "experimental" and is subject to change.
23+
*
24+
* See `FirestoreSettings.experimentalAutoDetectLongPolling`,
25+
* `FirestoreSettings.experimentalForceLongPolling`, and
26+
* `FirestoreSettings.experimentalLongPollingOptions`.
27+
*/
28+
export interface ExperimentalLongPollingOptions {
29+
/**
30+
* The desired maximum timeout interval, in seconds, to complete a
31+
* long-polling GET response. Valid values are between 5 and 30, inclusive.
32+
* Floating point values are allowed and will be rounded to the nearest
33+
* millisecond.
34+
*
35+
* By default, when long-polling is used the "hanging GET" request sent by
36+
* the client times out after 30 seconds. To request a different timeout
37+
* from the server, set this setting with the desired timeout.
38+
*
39+
* Changing the default timeout may be useful, for example, if the buffering
40+
* proxy that necessitated enabling long-polling in the first place has a
41+
* shorter timeout for hanging GET requests, in which case setting the
42+
* long-polling timeout to a shorter value, such as 25 seconds, may fix
43+
* prematurely-closed hanging GET requests.
44+
* For example, see https://github.com/firebase/firebase-js-sdk/issues/6987.
45+
*/
46+
timeoutSeconds?: number;
47+
}
48+
49+
/**
50+
* Compares two `ExperimentalLongPollingOptions` objects for equality.
51+
*/
52+
export function longPollingOptionsEqual(
53+
options1: ExperimentalLongPollingOptions,
54+
options2: ExperimentalLongPollingOptions
55+
): boolean {
56+
return options1.timeoutSeconds === options2.timeoutSeconds;
57+
}
58+
59+
/**
60+
* Creates and returns a new `ExperimentalLongPollingOptions` with the same
61+
* option values as the given instance.
62+
*/
63+
export function cloneLongPollingOptions(
64+
options: ExperimentalLongPollingOptions
65+
): ExperimentalLongPollingOptions {
66+
const clone: ExperimentalLongPollingOptions = {};
67+
68+
if (options.timeoutSeconds !== undefined) {
69+
clone.timeoutSeconds = options.timeoutSeconds;
70+
}
71+
72+
return clone;
73+
}

packages/firestore/src/api/settings.ts

Lines changed: 12 additions & 0 deletions
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 { ExperimentalLongPollingOptions } from './long_polling_options';
2122

2223
export { DEFAULT_HOST } from '../lite-api/settings';
2324

@@ -92,4 +93,15 @@ export interface FirestoreSettings extends LiteSettings {
9293
* cannot be combined with `experimentalForceLongPolling`.
9394
*/
9495
experimentalAutoDetectLongPolling?: boolean;
96+
97+
/**
98+
* Options that configure the SDK’s underlying network transport (WebChannel)
99+
* when long-polling is used.
100+
*
101+
* These options are only used if `experimentalForceLongPolling` is true or if
102+
* `experimentalAutoDetectLongPolling` is true and the auto-detection
103+
* determined that long-polling was needed. Otherwise, these options have no
104+
* effect.
105+
*/
106+
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
95107
}

packages/firestore/src/core/database_info.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FirebaseApp } from '@firebase/app';
22

3+
import { ExperimentalLongPollingOptions } from '../api/long_polling_options';
34
import { Code, FirestoreError } from '../util/error';
45

56
/**
@@ -34,6 +35,7 @@ export class DatabaseInfo {
3435
* when using WebChannel as the network transport.
3536
* @param autoDetectLongPolling - Whether to use the detectBufferingProxy
3637
* option when using WebChannel as the network transport.
38+
* @param longPollingOptions Options that configure long-polling.
3739
* @param useFetchStreams Whether to use the Fetch API instead of
3840
* XMLHTTPRequest
3941
*/
@@ -45,6 +47,7 @@ export class DatabaseInfo {
4547
readonly ssl: boolean,
4648
readonly forceLongPolling: boolean,
4749
readonly autoDetectLongPolling: boolean,
50+
readonly longPollingOptions: ExperimentalLongPollingOptions,
4851
readonly useFetchStreams: boolean
4952
) {}
5053
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import { _FirebaseService } from '@firebase/app';
2020

2121
import { CredentialsProvider } from '../api/credentials';
22+
import { cloneLongPollingOptions } from '../api/long_polling_options';
2223
import { User } from '../auth/user';
2324
import { DatabaseId, DatabaseInfo } from '../core/database_info';
2425
import { newConnection } from '../platform/connection';
@@ -117,6 +118,7 @@ export function makeDatabaseInfo(
117118
settings.ssl,
118119
settings.experimentalForceLongPolling,
119120
settings.experimentalAutoDetectLongPolling,
121+
cloneLongPollingOptions(settings.experimentalLongPollingOptions),
120122
settings.useFetchStreams
121123
);
122124
}

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717

1818
import { FirestoreLocalCache } from '../api/cache_config';
1919
import { CredentialsSettings } from '../api/credentials';
20+
import {
21+
ExperimentalLongPollingOptions,
22+
cloneLongPollingOptions,
23+
longPollingOptionsEqual
24+
} from '../api/long_polling_options';
2025
import {
2126
LRU_COLLECTION_DISABLED,
2227
LRU_DEFAULT_CACHE_SIZE_BYTES
@@ -29,6 +34,18 @@ import { validateIsNotUsedTogether } from '../util/input_validation';
2934
export const DEFAULT_HOST = 'firestore.googleapis.com';
3035
export const DEFAULT_SSL = true;
3136

37+
// The minimum long-polling timeout is hardcoded on the server. The value here
38+
// should be kept in sync with the value used by the server, as the server will
39+
// silently ignore a value below the minimum and fall back to the default.
40+
// Googlers see b/266868871 for relevant discussion.
41+
const MIN_LONG_POLLING_TIMEOUT_SECONDS = 5;
42+
43+
// No maximum long-polling timeout is configured in the server, and defaults to
44+
// 30 seconds, which is what Watch appears to use.
45+
// Googlers see b/266868871 for relevant discussion.
46+
const MAX_LONG_POLLING_TIMEOUT_SECONDS = 30;
47+
48+
// Whether long-polling auto-detected is enabled by default.
3249
const DEFAULT_AUTO_DETECT_LONG_POLLING = false;
3350

3451
/**
@@ -58,6 +75,7 @@ export interface PrivateSettings extends FirestoreSettings {
5875
cacheSizeBytes?: number;
5976
experimentalForceLongPolling?: boolean;
6077
experimentalAutoDetectLongPolling?: boolean;
78+
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
6179
useFetchStreams?: boolean;
6280

6381
localCache?: FirestoreLocalCache;
@@ -81,6 +99,8 @@ export class FirestoreSettingsImpl {
8199

82100
readonly experimentalAutoDetectLongPolling: boolean;
83101

102+
readonly experimentalLongPollingOptions: ExperimentalLongPollingOptions;
103+
84104
readonly ignoreUndefinedProperties: boolean;
85105

86106
readonly useFetchStreams: boolean;
@@ -146,6 +166,11 @@ export class FirestoreSettingsImpl {
146166
!!settings.experimentalAutoDetectLongPolling;
147167
}
148168

169+
this.experimentalLongPollingOptions = cloneLongPollingOptions(
170+
settings.experimentalLongPollingOptions ?? {}
171+
);
172+
validateLongPollingOptions(this.experimentalLongPollingOptions);
173+
149174
this.useFetchStreams = !!settings.useFetchStreams;
150175
}
151176

@@ -159,8 +184,40 @@ export class FirestoreSettingsImpl {
159184
other.experimentalForceLongPolling &&
160185
this.experimentalAutoDetectLongPolling ===
161186
other.experimentalAutoDetectLongPolling &&
187+
longPollingOptionsEqual(
188+
this.experimentalLongPollingOptions,
189+
other.experimentalLongPollingOptions
190+
) &&
162191
this.ignoreUndefinedProperties === other.ignoreUndefinedProperties &&
163192
this.useFetchStreams === other.useFetchStreams
164193
);
165194
}
166195
}
196+
197+
function validateLongPollingOptions(
198+
options: ExperimentalLongPollingOptions
199+
): void {
200+
if (options.timeoutSeconds !== undefined) {
201+
if (isNaN(options.timeoutSeconds)) {
202+
throw new FirestoreError(
203+
Code.INVALID_ARGUMENT,
204+
`invalid long polling timeout: ` +
205+
`${options.timeoutSeconds} (must not be NaN)`
206+
);
207+
}
208+
if (options.timeoutSeconds < MIN_LONG_POLLING_TIMEOUT_SECONDS) {
209+
throw new FirestoreError(
210+
Code.INVALID_ARGUMENT,
211+
`invalid long polling timeout: ${options.timeoutSeconds} ` +
212+
`(minimum allowed value is ${MIN_LONG_POLLING_TIMEOUT_SECONDS})`
213+
);
214+
}
215+
if (options.timeoutSeconds > MAX_LONG_POLLING_TIMEOUT_SECONDS) {
216+
throw new FirestoreError(
217+
Code.INVALID_ARGUMENT,
218+
`invalid long polling timeout: ${options.timeoutSeconds} ` +
219+
`(maximum allowed value is ${MAX_LONG_POLLING_TIMEOUT_SECONDS})`
220+
);
221+
}
222+
}
223+
}

0 commit comments

Comments
 (0)