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

fix: remove GCF transaction fallback #1112

Merged
merged 4 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 1 addition & 30 deletions dev/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,18 +363,6 @@ export class Firestore implements firestore.Firestore {
*/
private bulkWritersCount = 0;

// GCF currently tears down idle connections after two minutes. Requests
// that are issued after this period may fail. On GCF, we therefore issue
// these requests as part of a transaction so that we can safely retry until
// the network link is reestablished.
//
// The environment variable FUNCTION_TRIGGER_TYPE is used to detect the GCF
// environment.
/** @private */
_preferTransactions: boolean;
/** @private */
_lastSuccessfulRequest = 0;

/**
* @param {Object=} settings [Configuration object](#/docs).
* @param {string=} settings.projectId The project ID from the Google
Expand Down Expand Up @@ -449,20 +437,6 @@ export class Firestore implements firestore.Firestore {
backoffFactor: retryConfig.retry_delay_multiplier,
};

// GCF currently tears down idle connections after two minutes. Requests
// that are issued after this period may fail. On GCF, we therefore issue
// these requests as part of a transaction so that we can safely retry until
// the network link is reestablished.
//
// The environment variable FUNCTION_TRIGGER_TYPE is used to detect the GCF
// environment.
this._preferTransactions = process.env.FUNCTION_TRIGGER_TYPE !== undefined;
this._lastSuccessfulRequest = 0;

if (this._preferTransactions) {
logger('Firestore', null, 'Detected GCF environment');
}

const maxIdleChannels =
this._settings.maxIdleChannels === undefined
? DEFAULT_MAX_IDLE_CHANNELS
Expand Down Expand Up @@ -1284,9 +1258,7 @@ export class Firestore implements firestore.Firestore {

try {
await backoff.backoffAndWait();
const result = await func();
this._lastSuccessfulRequest = new Date().getTime();
return result;
return await func();
} catch (err) {
lastError = err;

Expand Down Expand Up @@ -1445,7 +1417,6 @@ export class Firestore implements firestore.Firestore {
'Received response: %j',
result
);
this._lastSuccessfulRequest = new Date().getTime();
return result;
} catch (err) {
logger('Firestore.request', requestTag, 'Received error:', err);
Expand Down
2 changes: 1 addition & 1 deletion dev/src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export class Transaction implements firestore.Transaction {
*/
commit(): Promise<void> {
return this._writeBatch
.commit_({
._commit({
transactionId: this._transactionId,
requestTag: this._requestTag,
})
Expand Down
46 changes: 5 additions & 41 deletions dev/src/write-batch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ export class WriteBatch implements firestore.WriteBatch {
// Capture the error stack to preserve stack tracing across async calls.
const stack = Error().stack!;

return this.commit_().catch(err => {
return this._commit().catch(err => {
throw wrapError(err, stack);
});
}
Expand Down Expand Up @@ -596,7 +596,7 @@ export class WriteBatch implements firestore.WriteBatch {
* this request.
* @returns A Promise that resolves when this batch completes.
*/
async commit_(commitOptions?: {
async _commit(commitOptions?: {
transactionId?: Uint8Array;
requestTag?: string;
}): Promise<WriteResult[]> {
Expand All @@ -608,26 +608,13 @@ export class WriteBatch implements firestore.WriteBatch {

const database = this._firestore.formattedName;

// On GCF, we periodically force transactional commits to allow for
// request retries in case GCF closes our backend connection.
const explicitTransaction = commitOptions && commitOptions.transactionId;
if (!explicitTransaction && this._shouldCreateTransaction()) {
logger('WriteBatch.commit', tag, 'Using transaction for commit');
return this._firestore
.request<api.IBeginTransactionRequest, api.IBeginTransactionResponse>(
'beginTransaction',
{database},
tag
)
.then(resp => {
return this.commit_({transactionId: resp.transaction!});
});
}

const request: api.ICommitRequest = {
database,
writes: this._ops.map(op => op()),
};
if (commitOptions?.transactionId) {
request.transaction = commitOptions.transactionId;
}

logger(
'WriteBatch.commit',
Expand All @@ -636,9 +623,6 @@ export class WriteBatch implements firestore.WriteBatch {
request.writes!.length
);

if (explicitTransaction) {
request.transaction = explicitTransaction;
}
const response = await this._firestore.request<
api.ICommitRequest,
api.CommitResponse
Expand All @@ -652,26 +636,6 @@ export class WriteBatch implements firestore.WriteBatch {
);
}

/**
* Determines whether we should issue a transactional commit. On GCF, this
* happens after two minutes of idleness.
*
* @private
* @returns Whether to use a transaction.
*/
private _shouldCreateTransaction(): boolean {
if (!this._firestore._preferTransactions) {
return false;
}

if (this._firestore._lastSuccessfulRequest) {
const now = new Date().getTime();
return now - this._firestore._lastSuccessfulRequest > GCF_IDLE_TIMEOUT_MS;
}

return true;
}

/**
* Resets the WriteBatch and dequeues all pending operations.
* @private
Expand Down
53 changes: 0 additions & 53 deletions dev/test/write-batch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,59 +433,6 @@ describe('batch support', () => {
});
});
});

it('uses transactions on GCF', () => {
// We use this environment variable during initialization to detect whether
// we are running on GCF.
process.env.FUNCTION_TRIGGER_TYPE = 'http-trigger';

let beginCalled = 0;
let commitCalled = 0;

const overrides: ApiOverride = {
beginTransaction: () => {
++beginCalled;
return response({transaction: Buffer.from('foo')});
},
commit: () => {
++commitCalled;
return response({
commitTime: {
nanos: 0,
seconds: 0,
},
});
},
};

return createInstance(overrides).then(firestore => {
firestore['_preferTransactions'] = true;
firestore['_lastSuccessfulRequest'] = 0;

return firestore
.batch()
.commit()
.then(() => {
// The first commit always uses a transcation.
expect(beginCalled).to.equal(1);
expect(commitCalled).to.equal(1);
return firestore.batch().commit();
})
.then(() => {
// The following commits don't use transactions if they happen
// within two minutes.
expect(beginCalled).to.equal(1);
expect(commitCalled).to.equal(2);
firestore['_lastSuccessfulRequest'] = 1337;
return firestore.batch().commit();
})
.then(() => {
expect(beginCalled).to.equal(2);
expect(commitCalled).to.equal(3);
delete process.env.FUNCTION_TRIGGER_TYPE;
});
});
});
});

describe('bulkCommit support', () => {
Expand Down