Skip to content

Commit fffdb75

Browse files
authored
Ensure that we create an empty TargetGlobal row. (#1029)
Ensure the v3 migration unconditionally creates the TargetGlobal row. Remove the no-longer-necessary v2 schema migration.
1 parent 4b51dee commit fffdb75

File tree

2 files changed

+53
-98
lines changed

2 files changed

+53
-98
lines changed

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import { SnapshotVersion } from '../core/snapshot_version';
2929
* Schema Version for the Web client:
3030
* 1. Initial version including Mutation Queue, Query Cache, and Remote Document
3131
* Cache
32-
* 2. Added targetCount to targetGlobal row.
32+
* 2. Used to ensure a targetGlobal object exists and add targetCount to it. No
33+
* longer required because migration 3 unconditionally clears it.
3334
* 3. Dropped and re-created Query Cache to deal with cache corruption related
3435
* to limbo resolution. Addresses
3536
* https://github.com/firebase/firebase-ios-sdk/issues/1548
@@ -61,21 +62,19 @@ export function createOrUpgradeDb(
6162
createRemoteDocumentCache(db);
6263
}
6364

64-
let p = PersistencePromise.resolve();
65-
if (fromVersion < 2 && toVersion >= 2) {
66-
p = ensureTargetGlobalExists(txn).next(targetGlobal =>
67-
saveTargetCount(txn, targetGlobal)
68-
);
69-
}
65+
// Migration 2 to populate the targetGlobal object no longer needed since
66+
// migration 3 unconditionally clears it.
7067

71-
p = p.next(() => {
68+
let p = PersistencePromise.resolve();
69+
if (fromVersion < 3 && toVersion >= 3) {
7270
// Brand new clients don't need to drop and recreate--only clients that
7371
// potentially have corrupt data.
74-
if (fromVersion !== 0 && fromVersion < 3 && toVersion >= 3) {
72+
if (fromVersion !== 0) {
7573
dropQueryCache(db);
7674
createQueryCache(db);
7775
}
78-
});
76+
p = p.next(() => writeEmptyTargetGlobalEntry(txn));
77+
}
7978

8079
return p;
8180
}
@@ -524,48 +523,23 @@ function dropQueryCache(db: IDBDatabase): void {
524523
}
525524

526525
/**
527-
* Counts the number of targets persisted and adds that value to the target
528-
* global singleton.
529-
*/
530-
function saveTargetCount(
531-
txn: SimpleDbTransaction,
532-
metadata: DbTargetGlobal
533-
): PersistencePromise<void> {
534-
const globalStore = txn.store<DbTargetGlobalKey, DbTargetGlobal>(
535-
DbTargetGlobal.store
536-
);
537-
const targetStore = txn.store<DbTargetKey, DbTarget>(DbTarget.store);
538-
return targetStore.count().next(count => {
539-
metadata.targetCount = count;
540-
return globalStore.put(DbTargetGlobal.key, metadata);
541-
});
542-
}
543-
544-
/**
545-
* Ensures that the target global singleton row exists by adding it if it's
546-
* missing.
526+
* Creates the target global singleton row.
547527
*
548528
* @param {IDBTransaction} txn The version upgrade transaction for indexeddb
549529
*/
550-
function ensureTargetGlobalExists(
530+
function writeEmptyTargetGlobalEntry(
551531
txn: SimpleDbTransaction
552-
): PersistencePromise<DbTargetGlobal> {
532+
): PersistencePromise<void> {
553533
const globalStore = txn.store<DbTargetGlobalKey, DbTargetGlobal>(
554534
DbTargetGlobal.store
555535
);
556-
return globalStore.get(DbTargetGlobal.key).next(metadata => {
557-
if (metadata != null) {
558-
return PersistencePromise.resolve(metadata);
559-
} else {
560-
metadata = new DbTargetGlobal(
561-
/*highestTargetId=*/ 0,
562-
/*lastListenSequenceNumber=*/ 0,
563-
SnapshotVersion.MIN.toTimestamp(),
564-
/*targetCount=*/ 0
565-
);
566-
return globalStore.put(DbTargetGlobal.key, metadata).next(() => metadata);
567-
}
568-
});
536+
const metadata = new DbTargetGlobal(
537+
/*highestTargetId=*/ 0,
538+
/*lastListenSequenceNumber=*/ 0,
539+
SnapshotVersion.MIN.toTimestamp(),
540+
/*targetCount=*/ 0
541+
);
542+
return globalStore.put(DbTargetGlobal.key, metadata);
569543
}
570544

571545
/**

packages/firestore/test/unit/local/indexeddb_schema.test.ts

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import {
2424
DbTarget,
2525
DbTargetGlobal,
2626
DbTargetGlobalKey,
27-
DbTargetKey
27+
DbTargetKey,
28+
DbTimestamp
2829
} from '../../../src/local/indexeddb_schema';
2930
import { SimpleDb, SimpleDbTransaction } from '../../../src/local/simple_db';
30-
import { PersistencePromise } from '../../../src/local/persistence_promise';
31+
import { SnapshotVersion } from '../../../src/core/snapshot_version';
3132

3233
const INDEXEDDB_TEST_DATABASE = 'schemaTest';
3334

@@ -71,17 +72,6 @@ function getAllObjectStores(db: IDBDatabase): string[] {
7172
return objectStores;
7273
}
7374

74-
function getTargetCount(db: IDBDatabase): Promise<number> {
75-
const sdb = new SimpleDb(db);
76-
return sdb
77-
.runTransaction('readonly', [DbTargetGlobal.store], txn =>
78-
txn
79-
.store<DbTargetGlobalKey, DbTargetGlobal>(DbTargetGlobal.store)
80-
.get(DbTargetGlobal.key)
81-
)
82-
.then(metadata => metadata.targetCount);
83-
}
84-
8575
describe('IndexedDbSchema: createOrUpgradeDb', () => {
8676
if (!IndexedDbPersistence.isAvailable()) {
8777
console.warn('No IndexedDB. Skipping createOrUpgradeDb() tests.');
@@ -101,58 +91,35 @@ describe('IndexedDbSchema: createOrUpgradeDb', () => {
10191
});
10292
});
10393

104-
it('can install schema version 2', () => {
105-
return withDb(2, db => {
106-
expect(db.version).to.equal(2);
107-
// We should have all of the stores, we should have the target global row
108-
// and we should not have any targets counted, because there are none.
109-
expect(getAllObjectStores(db)).to.have.members(ALL_STORES);
110-
// Check the target count. We haven't added any targets, so we expect 0.
111-
return getTargetCount(db).then(targetCount => {
112-
expect(targetCount).to.equal(0);
113-
});
114-
});
115-
});
116-
117-
it('can upgrade from schema version 1 to 2', () => {
118-
const expectedTargetCount = 5;
119-
return withDb(1, db => {
120-
const sdb = new SimpleDb(db);
121-
// Now that we have all of the stores, add some targets so the next
122-
// migration can count them.
123-
return sdb.runTransaction('readwrite', [DbTarget.store], txn => {
124-
const store = txn.store(DbTarget.store);
125-
let p = PersistencePromise.resolve();
126-
for (let i = 0; i < expectedTargetCount; i++) {
127-
p = p.next(() => store.put({ targetId: i }));
128-
}
129-
return p;
130-
});
131-
}).then(() =>
132-
withDb(2, db => {
133-
expect(db.version).to.equal(2);
134-
expect(getAllObjectStores(db)).to.have.members(ALL_STORES);
135-
return getTargetCount(db).then(targetCount => {
136-
expect(targetCount).to.equal(expectedTargetCount);
137-
});
138-
})
139-
);
140-
});
141-
14294
it('drops the query cache from 2 to 3', () => {
14395
const userId = 'user';
14496
const batchId = 1;
14597
const targetId = 2;
14698

14799
const expectedMutation = new DbMutationBatch(userId, batchId, 1000, []);
100+
const dummyTargetGlobal = new DbTargetGlobal(
101+
/*highestTargetId=*/ 1,
102+
/*highestListenSequencNumber=*/ 1,
103+
/*lastRemoteSnapshotVersion=*/ new DbTimestamp(1, 1),
104+
/*targetCount=*/ 1
105+
);
106+
const resetTargetGlobal = new DbTargetGlobal(
107+
/*highestTargetId=*/ 0,
108+
/*highestListenSequencNumber=*/ 0,
109+
/*lastRemoteSnapshotVersion=*/ SnapshotVersion.MIN.toTimestamp(),
110+
/*targetCount=*/ 0
111+
);
148112

149113
return withDb(2, db => {
150114
const sdb = new SimpleDb(db);
151115
return sdb.runTransaction(
152116
'readwrite',
153-
[DbTarget.store, DbMutationBatch.store],
117+
[DbTarget.store, DbTargetGlobal.store, DbMutationBatch.store],
154118
txn => {
155119
const targets = txn.store<DbTargetKey, DbTarget>(DbTarget.store);
120+
const targetGlobal = txn.store<DbTargetGlobalKey, DbTargetGlobal>(
121+
DbTargetGlobal.store
122+
);
156123
const mutations = txn.store<DbMutationBatchKey, DbMutationBatch>(
157124
DbMutationBatch.store
158125
);
@@ -161,6 +128,9 @@ describe('IndexedDbSchema: createOrUpgradeDb', () => {
161128
targets
162129
// tslint:disable-next-line:no-any
163130
.put({ targetId, canonicalId: 'foo' } as any)
131+
.next(() =>
132+
targetGlobal.put(DbTargetGlobal.key, dummyTargetGlobal)
133+
)
164134
.next(() => mutations.put(expectedMutation))
165135
);
166136
}
@@ -173,9 +143,12 @@ describe('IndexedDbSchema: createOrUpgradeDb', () => {
173143
const sdb = new SimpleDb(db);
174144
return sdb.runTransaction(
175145
'readwrite',
176-
[DbTarget.store, DbMutationBatch.store],
146+
[DbTarget.store, DbTargetGlobal.store, DbMutationBatch.store],
177147
txn => {
178148
const targets = txn.store<DbTargetKey, DbTarget>(DbTarget.store);
149+
const targetGlobal = txn.store<DbTargetGlobalKey, DbTargetGlobal>(
150+
DbTargetGlobal.store
151+
);
179152
const mutations = txn.store<DbMutationBatchKey, DbMutationBatch>(
180153
DbMutationBatch.store
181154
);
@@ -186,6 +159,14 @@ describe('IndexedDbSchema: createOrUpgradeDb', () => {
186159
// The target should have been dropped
187160
expect(target).to.be.null;
188161
})
162+
.next(() => targetGlobal.get(DbTargetGlobal.key))
163+
.next(targetGlobalEntry => {
164+
// Target Global should exist but be cleared.
165+
// HACK: round-trip through JSON to clear types, like IndexedDb
166+
// does.
167+
const expected = JSON.parse(JSON.stringify(resetTargetGlobal));
168+
expect(targetGlobalEntry).to.deep.equal(expected);
169+
})
189170
.next(() => mutations.get([userId, batchId]))
190171
.next(mutation => {
191172
// Mutations should be unaffected.

0 commit comments

Comments
 (0)