@@ -21,9 +21,13 @@ import { ResourcePath } from '../model/path';
21
21
import { assert } from '../util/assert' ;
22
22
23
23
import { encode , EncodedResourcePath } from './encoded_resource_path' ;
24
- import { SimpleDbTransaction } from './simple_db' ;
24
+ import { SimpleDbSchemaConverter , SimpleDbTransaction } from './simple_db' ;
25
25
import { PersistencePromise } from './persistence_promise' ;
26
26
import { SnapshotVersion } from '../core/snapshot_version' ;
27
+ import { BATCHID_UNKNOWN } from '../model/mutation_batch' ;
28
+ import { IndexedDbMutationQueue } from './indexeddb_mutation_queue' ;
29
+ import { LocalSerializer } from './local_serializer' ;
30
+ import { IndexedDbTransaction } from './indexeddb_persistence' ;
27
31
28
32
/**
29
33
* Schema Version for the Web client:
@@ -35,66 +39,122 @@ import { SnapshotVersion } from '../core/snapshot_version';
35
39
* to limbo resolution. Addresses
36
40
* https://github.com/firebase/firebase-ios-sdk/issues/1548
37
41
* 4. Multi-Tab Support.
42
+ * 5. Removal of held write acks (not yet active).
38
43
*/
39
44
export const SCHEMA_VERSION = 4 ;
45
+ // TODO(mrschmidt): As SCHEMA_VERSION becomes 5, uncomment the assert in
46
+ // `createOrUpgrade`.
40
47
41
- /**
42
- * Performs database creation and schema upgrades.
43
- *
44
- * Note that in production, this method is only ever used to upgrade the schema
45
- * to SCHEMA_VERSION. Different values of toVersion are only used for testing
46
- * and local feature development.
47
- */
48
- export function createOrUpgradeDb (
49
- db : IDBDatabase ,
50
- txn : SimpleDbTransaction ,
51
- fromVersion : number ,
52
- toVersion : number
53
- ) : PersistencePromise < void > {
54
- assert (
55
- fromVersion < toVersion && fromVersion >= 0 && toVersion <= SCHEMA_VERSION ,
56
- 'Unexpected schema upgrade from v${fromVersion} to v{toVersion}.'
57
- ) ;
48
+ /** Performs database creation and schema upgrades. */
49
+ export class SchemaConverter implements SimpleDbSchemaConverter {
50
+ constructor ( private readonly serializer : LocalSerializer ) { }
58
51
59
- if ( fromVersion < 1 && toVersion >= 1 ) {
60
- createPrimaryClientStore ( db ) ;
61
- createMutationQueue ( db ) ;
62
- createQueryCache ( db ) ;
63
- createRemoteDocumentCache ( db ) ;
64
- }
52
+ /**
53
+ * Performs database creation and schema upgrades.
54
+ *
55
+ * Note that in production, this method is only ever used to upgrade the schema
56
+ * to SCHEMA_VERSION. Different values of toVersion are only used for testing
57
+ * and local feature development.
58
+ */
59
+ createOrUpgrade (
60
+ db : IDBDatabase ,
61
+ txn : SimpleDbTransaction ,
62
+ fromVersion : number ,
63
+ toVersion : number
64
+ ) : PersistencePromise < void > {
65
+ // assert(
66
+ // fromVersion < toVersion && fromVersion >= 0 && toVersion <= SCHEMA_VERSION,
67
+ // `Unexpected schema upgrade from v${fromVersion} to v{toVersion}.`
68
+ // );
69
+
70
+ if ( fromVersion < 1 && toVersion >= 1 ) {
71
+ createPrimaryClientStore ( db ) ;
72
+ createMutationQueue ( db ) ;
73
+ createQueryCache ( db ) ;
74
+ createRemoteDocumentCache ( db ) ;
75
+ }
65
76
66
- // Migration 2 to populate the targetGlobal object no longer needed since
67
- // migration 3 unconditionally clears it.
77
+ // Migration 2 to populate the targetGlobal object no longer needed since
78
+ // migration 3 unconditionally clears it.
79
+
80
+ let p = PersistencePromise . resolve ( ) ;
81
+ if ( fromVersion < 3 && toVersion >= 3 ) {
82
+ // Brand new clients don't need to drop and recreate--only clients that
83
+ // potentially have corrupt data.
84
+ if ( fromVersion !== 0 ) {
85
+ dropQueryCache ( db ) ;
86
+ createQueryCache ( db ) ;
87
+ }
88
+ p = p . next ( ( ) => writeEmptyTargetGlobalEntry ( txn ) ) ;
89
+ }
68
90
69
- let p = PersistencePromise . resolve ( ) ;
70
- if ( fromVersion < 3 && toVersion >= 3 ) {
71
- // Brand new clients don't need to drop and recreate--only clients that
72
- // potentially have corrupt data.
73
- if ( fromVersion !== 0 ) {
74
- dropQueryCache ( db ) ;
75
- createQueryCache ( db ) ;
91
+ if ( fromVersion < 4 && toVersion >= 4 ) {
92
+ if ( fromVersion !== 0 ) {
93
+ // Schema version 3 uses auto-generated keys to generate globally unique
94
+ // mutation batch IDs (this was previously ensured internally by the
95
+ // client). To migrate to the new schema, we have to read all mutations
96
+ // and write them back out. We preserve the existing batch IDs to guarantee
97
+ // consistency with other object stores. Any further mutation batch IDs will
98
+ // be auto-generated.
99
+ p = p . next ( ( ) => upgradeMutationBatchSchemaAndMigrateData ( db , txn ) ) ;
100
+ }
101
+
102
+ p = p . next ( ( ) => {
103
+ createClientMetadataStore ( db ) ;
104
+ createRemoteDocumentChangesStore ( db ) ;
105
+ } ) ;
76
106
}
77
- p = p . next ( ( ) => writeEmptyTargetGlobalEntry ( txn ) ) ;
78
- }
79
107
80
- if ( fromVersion < 4 && toVersion >= 4 ) {
81
- if ( fromVersion !== 0 ) {
82
- // Schema version 3 uses auto-generated keys to generate globally unique
83
- // mutation batch IDs (this was previously ensured internally by the
84
- // client). To migrate to the new schema, we have to read all mutations
85
- // and write them back out. We preserve the existing batch IDs to guarantee
86
- // consistency with other object stores. Any further mutation batch IDs will
87
- // be auto-generated.
88
- p = p . next ( ( ) => upgradeMutationBatchSchemaAndMigrateData ( db , txn ) ) ;
108
+ if ( fromVersion < 5 && toVersion >= 5 ) {
109
+ p = p . next ( ( ) => this . removeAcknowledgedMutations ( txn ) ) ;
89
110
}
90
111
91
- p = p . next ( ( ) => {
92
- createClientMetadataStore ( db ) ;
93
- createRemoteDocumentChangesStore ( db ) ;
94
- } ) ;
112
+ return p ;
95
113
}
96
114
97
- return p ;
115
+ private removeAcknowledgedMutations (
116
+ txn : SimpleDbTransaction
117
+ ) : PersistencePromise < void > {
118
+ const queuesStore = txn . store < DbMutationQueueKey , DbMutationQueue > (
119
+ DbMutationQueue . store
120
+ ) ;
121
+ const mutationsStore = txn . store < DbMutationBatchKey , DbMutationBatch > (
122
+ DbMutationBatch . store
123
+ ) ;
124
+
125
+ const indexedDbTransaction = new IndexedDbTransaction ( txn ) ;
126
+ return queuesStore . loadAll ( ) . next ( queues => {
127
+ return PersistencePromise . forEach ( queues , queue => {
128
+ const mutationQueue = new IndexedDbMutationQueue (
129
+ queue . userId ,
130
+ this . serializer
131
+ ) ;
132
+ const range = IDBKeyRange . bound (
133
+ [ queue . userId , BATCHID_UNKNOWN ] ,
134
+ [ queue . userId , queue . lastAcknowledgedBatchId ]
135
+ ) ;
136
+
137
+ return mutationsStore
138
+ . loadAll ( DbMutationBatch . userMutationsIndex , range )
139
+ . next ( dbBatches => {
140
+ return PersistencePromise . forEach ( dbBatches , dbBatch => {
141
+ assert (
142
+ dbBatch . userId === queue . userId ,
143
+ `Cannot process batch ${ dbBatch . batchId } from unexpected user`
144
+ ) ;
145
+ const batch = this . serializer . fromDbMutationBatch ( dbBatch ) ;
146
+ return mutationQueue . removeMutationBatch (
147
+ indexedDbTransaction ,
148
+ batch
149
+ ) ;
150
+ } ) ;
151
+ } )
152
+ . next ( ( ) =>
153
+ mutationQueue . performConsistencyCheck ( indexedDbTransaction )
154
+ ) ;
155
+ } ) ;
156
+ } ) ;
157
+ }
98
158
}
99
159
100
160
// TODO(mikelehen): Get rid of "as any" if/when TypeScript fixes their types.
0 commit comments