15
15
*/
16
16
17
17
import { User } from '../auth/user' ;
18
+ import { DocumentKey } from '../model/document_key' ;
18
19
import { debug } from '../util/log' ;
20
+ import { ObjectMap } from '../util/obj_map' ;
21
+ import { encode } from './encoded_resource_path' ;
22
+ import {
23
+ ActiveTargets ,
24
+ LruDelegate ,
25
+ LruGarbageCollector
26
+ } from './lru_garbage_collector' ;
19
27
20
28
import { MemoryMutationQueue } from './memory_mutation_queue' ;
21
29
import { MemoryQueryCache } from './memory_query_cache' ;
@@ -24,14 +32,16 @@ import { MutationQueue } from './mutation_queue';
24
32
import {
25
33
Persistence ,
26
34
PersistenceTransaction ,
27
- PrimaryStateListener
35
+ PrimaryStateListener ,
36
+ ReferenceDelegate
28
37
} from './persistence' ;
29
38
import { PersistencePromise } from './persistence_promise' ;
30
- import { QueryCache } from './query_cache ' ;
31
- import { RemoteDocumentCache } from './remote_document_cache ' ;
39
+ import { QueryData } from './query_data ' ;
40
+ import { ReferenceSet } from './reference_set ' ;
32
41
import { ClientId } from './shared_client_state' ;
33
42
import { ListenSequenceNumber } from '../core/types' ;
34
43
import { ListenSequence } from '../core/listen_sequence' ;
44
+ import * as obj from '../util/obj' ;
35
45
36
46
const LOG_TAG = 'MemoryPersistence' ;
37
47
@@ -49,23 +59,39 @@ export class MemoryPersistence implements Persistence {
49
59
*/
50
60
private mutationQueues : { [ user : string ] : MutationQueue } = { } ;
51
61
private remoteDocumentCache = new MemoryRemoteDocumentCache ( ) ;
52
- private queryCache = new MemoryQueryCache ( ) ;
62
+ private readonly queryCache : MemoryQueryCache ; // = new MemoryQueryCache();
63
+ private readonly listenSequence = new ListenSequence ( 0 ) ;
53
64
54
65
private _started = false ;
55
66
67
+ // TODO(gsoltis): remove option to be null once eager delegate is implemented.
68
+ private _referenceDelegate : ReferenceDelegate | null ;
69
+
70
+ static createLruPersistence ( clientId : ClientId ) : MemoryPersistence {
71
+ const persistence = new MemoryPersistence ( clientId ) ;
72
+ persistence . _referenceDelegate = new MemoryLruDelegate ( persistence ) ;
73
+ return persistence ;
74
+ }
75
+
56
76
constructor ( private readonly clientId : ClientId ) {
57
77
this . _started = true ;
78
+ this . queryCache = new MemoryQueryCache ( this ) ;
58
79
}
59
80
60
- async shutdown ( deleteData ?: boolean ) : Promise < void > {
81
+ shutdown ( deleteData ?: boolean ) : Promise < void > {
61
82
// No durable state to ensure is closed on shutdown.
62
83
this . _started = false ;
84
+ return Promise . resolve ( ) ;
63
85
}
64
86
65
87
get started ( ) : boolean {
66
88
return this . _started ;
67
89
}
68
90
91
+ get referenceDelegate ( ) : ReferenceDelegate {
92
+ return this . _referenceDelegate ! ;
93
+ }
94
+
69
95
async getActiveClients ( ) : Promise < ClientId [ ] > {
70
96
return [ this . clientId ] ;
71
97
}
@@ -90,11 +116,11 @@ export class MemoryPersistence implements Persistence {
90
116
return queue ;
91
117
}
92
118
93
- getQueryCache ( ) : QueryCache {
119
+ getQueryCache ( ) : MemoryQueryCache {
94
120
return this . queryCache ;
95
121
}
96
122
97
- getRemoteDocumentCache ( ) : RemoteDocumentCache {
123
+ getRemoteDocumentCache ( ) : MemoryRemoteDocumentCache {
98
124
return this . remoteDocumentCache ;
99
125
}
100
126
@@ -107,9 +133,20 @@ export class MemoryPersistence implements Persistence {
107
133
) : Promise < T > {
108
134
debug ( LOG_TAG , 'Starting transaction:' , action ) ;
109
135
return transactionOperation (
110
- new MemoryTransaction ( ListenSequence . INVALID )
136
+ new MemoryTransaction ( this . listenSequence . next ( ) )
111
137
) . toPromise ( ) ;
112
138
}
139
+
140
+ mutationQueuesContainKey (
141
+ transaction : PersistenceTransaction ,
142
+ key : DocumentKey
143
+ ) : PersistencePromise < boolean > {
144
+ return PersistencePromise . or (
145
+ obj
146
+ . values ( this . mutationQueues )
147
+ . map ( queue => ( ) => queue . containsKey ( transaction , key ) )
148
+ ) ;
149
+ }
113
150
}
114
151
115
152
/**
@@ -119,3 +156,131 @@ export class MemoryPersistence implements Persistence {
119
156
export class MemoryTransaction implements PersistenceTransaction {
120
157
constructor ( readonly currentSequenceNumber : ListenSequenceNumber ) { }
121
158
}
159
+
160
+ export class MemoryLruDelegate implements ReferenceDelegate , LruDelegate {
161
+ private additionalReferences : ReferenceSet | null ;
162
+ private orphanedSequenceNumbers : ObjectMap <
163
+ DocumentKey ,
164
+ ListenSequenceNumber
165
+ > = new ObjectMap ( k => encode ( k . path ) ) ;
166
+
167
+ readonly garbageCollector : LruGarbageCollector ;
168
+
169
+ constructor ( private readonly persistence : MemoryPersistence ) {
170
+ this . garbageCollector = new LruGarbageCollector ( this ) ;
171
+ }
172
+
173
+ forEachTarget (
174
+ txn : PersistenceTransaction ,
175
+ f : ( q : QueryData ) => void
176
+ ) : PersistencePromise < void > {
177
+ return this . persistence . getQueryCache ( ) . forEachTarget ( txn , f ) ;
178
+ }
179
+
180
+ getTargetCount ( txn : PersistenceTransaction ) : PersistencePromise < number > {
181
+ return this . persistence . getQueryCache ( ) . getTargetCount ( txn ) ;
182
+ }
183
+
184
+ forEachOrphanedDocumentSequenceNumber (
185
+ txn : PersistenceTransaction ,
186
+ f : ( sequenceNumber : ListenSequenceNumber ) => void
187
+ ) : PersistencePromise < void > {
188
+ this . orphanedSequenceNumbers . forEach ( ( _ , sequenceNumber ) =>
189
+ f ( sequenceNumber )
190
+ ) ;
191
+ return PersistencePromise . resolve ( ) ;
192
+ }
193
+
194
+ setInMemoryPins ( inMemoryPins : ReferenceSet ) : void {
195
+ this . additionalReferences = inMemoryPins ;
196
+ }
197
+
198
+ removeTargets (
199
+ txn : PersistenceTransaction ,
200
+ upperBound : ListenSequenceNumber ,
201
+ activeTargetIds : ActiveTargets
202
+ ) : PersistencePromise < number > {
203
+ return this . persistence
204
+ . getQueryCache ( )
205
+ . removeTargets ( txn , upperBound , activeTargetIds ) ;
206
+ }
207
+
208
+ removeOrphanedDocuments (
209
+ txn : PersistenceTransaction ,
210
+ upperBound : ListenSequenceNumber
211
+ ) : PersistencePromise < number > {
212
+ let count = 0 ;
213
+ const cache = this . persistence . getRemoteDocumentCache ( ) ;
214
+ const p = cache . forEachDocumentKey ( txn , key => {
215
+ return this . isPinned ( txn , key , upperBound ) . next ( isPinned => {
216
+ if ( isPinned ) {
217
+ return PersistencePromise . resolve ( ) ;
218
+ } else {
219
+ count ++ ;
220
+ return cache . removeEntry ( txn , key ) ;
221
+ }
222
+ } ) ;
223
+ } ) ;
224
+ return p . next ( ( ) => count ) ;
225
+ }
226
+
227
+ removeMutationReference (
228
+ txn : PersistenceTransaction ,
229
+ key : DocumentKey
230
+ ) : PersistencePromise < void > {
231
+ this . orphanedSequenceNumbers . set ( key , txn . currentSequenceNumber ) ;
232
+ return PersistencePromise . resolve ( ) ;
233
+ }
234
+
235
+ removeTarget (
236
+ txn : PersistenceTransaction ,
237
+ queryData : QueryData
238
+ ) : PersistencePromise < void > {
239
+ const updated = queryData . copy ( {
240
+ sequenceNumber : txn . currentSequenceNumber
241
+ } ) ;
242
+ return this . persistence . getQueryCache ( ) . updateQueryData ( txn , updated ) ;
243
+ }
244
+
245
+ addReference (
246
+ txn : PersistenceTransaction ,
247
+ key : DocumentKey
248
+ ) : PersistencePromise < void > {
249
+ this . orphanedSequenceNumbers . set ( key , txn . currentSequenceNumber ) ;
250
+ return PersistencePromise . resolve ( ) ;
251
+ }
252
+
253
+ removeReference (
254
+ txn : PersistenceTransaction ,
255
+ key : DocumentKey
256
+ ) : PersistencePromise < void > {
257
+ this . orphanedSequenceNumbers . set ( key , txn . currentSequenceNumber ) ;
258
+ return PersistencePromise . resolve ( ) ;
259
+ }
260
+
261
+ updateLimboDocument (
262
+ txn : PersistenceTransaction ,
263
+ key : DocumentKey
264
+ ) : PersistencePromise < void > {
265
+ this . orphanedSequenceNumbers . set ( key , txn . currentSequenceNumber ) ;
266
+ return PersistencePromise . resolve ( ) ;
267
+ }
268
+
269
+ private isPinned (
270
+ txn : PersistenceTransaction ,
271
+ key : DocumentKey ,
272
+ upperBound : ListenSequenceNumber
273
+ ) : PersistencePromise < boolean > {
274
+ return PersistencePromise . or ( [
275
+ ( ) => this . persistence . mutationQueuesContainKey ( txn , key ) ,
276
+ ( ) => this . additionalReferences ! . containsKey ( txn , key ) ,
277
+ ( ) => this . persistence . getQueryCache ( ) . containsKey ( txn , key ) ,
278
+ ( ) => {
279
+ const orphanedAt = this . orphanedSequenceNumbers . get ( key ) ;
280
+ return PersistencePromise . resolve (
281
+ orphanedAt !== undefined && orphanedAt > upperBound
282
+ ) ;
283
+ }
284
+ ] ) ;
285
+ }
286
+ }
0 commit comments