@@ -33,28 +33,29 @@ class TComputationPatternLRUCache::TLRUPatternCacheImpl
33
33
return CurrentPatternsCompiledCodeSizeInBytes;
34
34
}
35
35
36
- std::shared_ptr<TPatternCacheEntry>* Find (const TString& serializedProgram) {
36
+ TPatternCacheEntryPtr Find (const TString& serializedProgram) {
37
37
auto it = SerializedProgramToPatternCacheHolder.find (serializedProgram);
38
38
if (it == SerializedProgramToPatternCacheHolder.end ()) {
39
- return nullptr ;
39
+ return {} ;
40
40
}
41
41
42
42
PromoteEntry (&it->second );
43
43
44
- return & it->second .Entry ;
44
+ return it->second .Entry ;
45
45
}
46
46
47
- void Insert (const TString& serializedProgram, std::shared_ptr<TPatternCacheEntry> & entry) {
47
+ void Insert (const TString& serializedProgram, TPatternCacheEntryPtr & entry) {
48
48
auto [it, inserted] = SerializedProgramToPatternCacheHolder.emplace (std::piecewise_construct,
49
49
std::forward_as_tuple (serializedProgram),
50
50
std::forward_as_tuple (serializedProgram, entry));
51
51
52
52
if (!inserted) {
53
53
RemoveEntryFromLists (&it->second );
54
+ entry = it->second .Entry ;
55
+ } else {
56
+ entry->UpdateSizeForCache ();
54
57
}
55
58
56
- entry->UpdateSizeForCache ();
57
-
58
59
// / New item is inserted, insert it in the back of both LRU lists and recalculate sizes
59
60
CurrentPatternsSizeBytes += entry->SizeForCache ;
60
61
LRUPatternList.PushBack (&it->second );
@@ -69,15 +70,20 @@ class TComputationPatternLRUCache::TLRUPatternCacheImpl
69
70
ClearIfNeeded ();
70
71
}
71
72
72
- void NotifyPatternCompiled (const TString & serializedProgram) {
73
+ void NotifyPatternCompiled (const TString& serializedProgram) {
73
74
auto it = SerializedProgramToPatternCacheHolder.find (serializedProgram);
74
75
if (it == SerializedProgramToPatternCacheHolder.end ()) {
75
76
return ;
76
77
}
77
78
78
79
const auto & entry = it->second .Entry ;
79
80
80
- Y_ASSERT (entry->Pattern ->IsCompiled ());
81
+ if (!entry->Pattern ->IsCompiled ()) {
82
+ // This is possible if the old entry got removed from cache while being compiled - and the new entry got in.
83
+ // TODO: add metrics for this inefficient cache usage.
84
+ // TODO: make this scenario more consistent - don't waste compilation result.
85
+ return ;
86
+ }
81
87
82
88
if (it->second .LinkedInCompiledPatternLRUList ()) {
83
89
return ;
@@ -113,7 +119,7 @@ class TComputationPatternLRUCache::TLRUPatternCacheImpl
113
119
* Most recently accessed items are in back of the lists, least recently accessed items are in front of the lists.
114
120
*/
115
121
struct TPatternCacheHolder : public TIntrusiveListItem <TPatternCacheHolder, TPatternLRUListTag>, TIntrusiveListItem<TPatternCacheHolder, TCompiledPatternLRUListTag> {
116
- TPatternCacheHolder (TString serializedProgram, std::shared_ptr<TPatternCacheEntry> entry)
122
+ TPatternCacheHolder (TString serializedProgram, TPatternCacheEntryPtr entry)
117
123
: SerializedProgram(std::move(serializedProgram))
118
124
, Entry(std::move(entry))
119
125
{}
@@ -126,8 +132,8 @@ class TComputationPatternLRUCache::TLRUPatternCacheImpl
126
132
return !TIntrusiveListItem<TPatternCacheHolder, TCompiledPatternLRUListTag>::Empty ();
127
133
}
128
134
129
- TString SerializedProgram;
130
- std::shared_ptr<TPatternCacheEntry> Entry;
135
+ const TString SerializedProgram;
136
+ TPatternCacheEntryPtr Entry;
131
137
};
132
138
133
139
void PromoteEntry (TPatternCacheHolder* holder) {
@@ -228,52 +234,51 @@ TComputationPatternLRUCache::~TComputationPatternLRUCache() {
228
234
CleanCache ();
229
235
}
230
236
231
- std::shared_ptr<TPatternCacheEntry> TComputationPatternLRUCache::Find (const TString& serializedProgram) {
237
+ TPatternCacheEntryPtr TComputationPatternLRUCache::Find (const TString& serializedProgram) {
232
238
std::lock_guard<std::mutex> lock (Mutex);
233
239
if (auto it = Cache->Find (serializedProgram)) {
234
240
++*Hits;
235
241
236
- if ((*it) ->Pattern ->IsCompiled ())
242
+ if (it ->Pattern ->IsCompiled ())
237
243
++*HitsCompiled;
238
244
239
- return * it;
245
+ return it;
240
246
}
241
247
242
248
++*Misses;
243
249
return {};
244
250
}
245
251
246
- TComputationPatternLRUCache::TTicket TComputationPatternLRUCache::FindOrSubscribe (const TString& serializedProgram) {
252
+ TPatternCacheEntryFuture TComputationPatternLRUCache::FindOrSubscribe (const TString& serializedProgram) {
247
253
std::lock_guard lock (Mutex);
248
254
if (auto it = Cache->Find (serializedProgram)) {
249
255
++*Hits;
250
- AccessPattern (serializedProgram, * it);
251
- return TTicket (serializedProgram, false , NThreading::MakeFuture<std::shared_ptr<TPatternCacheEntry>>(*it), nullptr );
256
+ AccessPattern (serializedProgram, it);
257
+ return NThreading::MakeFuture<TPatternCacheEntryPtr>(it );
252
258
}
253
259
254
- auto [notifyIt, isNew] = Notify.emplace (serializedProgram, Nothing ());
260
+ auto [notifyIt, isNew] = Notify.emplace (std::piecewise_construct, std::forward_as_tuple ( serializedProgram), std::forward_as_tuple ());
255
261
if (isNew) {
256
262
++*Misses;
257
- return TTicket (serializedProgram, true , {}, this );
263
+ // First future is empty - so the subscriber can initiate the entry creation.
264
+ return {};
258
265
}
259
266
260
267
++*Waits;
261
- auto promise = NThreading::NewPromise<std::shared_ptr<TPatternCacheEntry> >();
268
+ auto promise = NThreading::NewPromise<TPatternCacheEntryPtr >();
262
269
auto & subscribers = notifyIt->second ;
263
- if (!subscribers) {
264
- subscribers.ConstructInPlace ();
265
- }
270
+ subscribers.push_back (promise);
266
271
267
- subscribers-> push_back (promise);
268
- return TTicket (serializedProgram, false , promise, nullptr ) ;
272
+ // Second and next futures are not empty - so subscribers can wait while first one creates the entry.
273
+ return promise;
269
274
}
270
275
271
- void TComputationPatternLRUCache::EmplacePattern (const TString& serializedProgram, std::shared_ptr<TPatternCacheEntry> patternWithEnv) {
276
+ void TComputationPatternLRUCache::EmplacePattern (const TString& serializedProgram, TPatternCacheEntryPtr& patternWithEnv) {
272
277
Y_DEBUG_ABORT_UNLESS (patternWithEnv && patternWithEnv->Pattern );
273
- TMaybe< TVector<NThreading::TPromise<std::shared_ptr<TPatternCacheEntry>> >> subscribers;
278
+ TVector<NThreading::TPromise<TPatternCacheEntryPtr >> subscribers;
274
279
275
280
{
276
- std::lock_guard<std::mutex> lock (Mutex);
281
+ std::lock_guard lock (Mutex);
277
282
Cache->Insert (serializedProgram, patternWithEnv);
278
283
279
284
auto notifyIt = Notify.find (serializedProgram);
@@ -288,10 +293,8 @@ void TComputationPatternLRUCache::EmplacePattern(const TString& serializedProgra
288
293
*SizeCompiledBytes = Cache->PatternsCompiledCodeSizeInBytes ();
289
294
}
290
295
291
- if (subscribers) {
292
- for (auto & subscriber : *subscribers) {
293
- subscriber.SetValue (patternWithEnv);
294
- }
296
+ for (auto & subscriber : subscribers) {
297
+ subscriber.SetValue (patternWithEnv);
295
298
}
296
299
}
297
300
@@ -300,6 +303,24 @@ void TComputationPatternLRUCache::NotifyPatternCompiled(const TString& serialize
300
303
Cache->NotifyPatternCompiled (serializedProgram);
301
304
}
302
305
306
+ void TComputationPatternLRUCache::NotifyPatternMissing (const TString& serializedProgram) {
307
+ TVector<NThreading::TPromise<std::shared_ptr<TPatternCacheEntry>>> subscribers;
308
+ {
309
+ std::lock_guard lock (Mutex);
310
+
311
+ auto notifyIt = Notify.find (serializedProgram);
312
+ if (notifyIt != Notify.end ()) {
313
+ subscribers.swap (notifyIt->second );
314
+ Notify.erase (notifyIt);
315
+ }
316
+ }
317
+
318
+ for (auto & subscriber : subscribers) {
319
+ // It's part of API - to set nullptr as broken promise.
320
+ subscriber.SetValue (nullptr );
321
+ }
322
+ }
323
+
303
324
size_t TComputationPatternLRUCache::GetSize () const {
304
325
std::lock_guard lock (Mutex);
305
326
return Cache->PatternsSize ();
@@ -314,7 +335,7 @@ void TComputationPatternLRUCache::CleanCache() {
314
335
Cache->Clear ();
315
336
}
316
337
317
- void TComputationPatternLRUCache::AccessPattern (const TString & serializedProgram, std::shared_ptr<TPatternCacheEntry> & entry) {
338
+ void TComputationPatternLRUCache::AccessPattern (const TString& serializedProgram, TPatternCacheEntryPtr entry) {
318
339
if (!Configuration.PatternAccessTimesBeforeTryToCompile || entry->Pattern ->IsCompiled ()) {
319
340
return ;
320
341
}
@@ -326,22 +347,4 @@ void TComputationPatternLRUCache::AccessPattern(const TString & serializedProgra
326
347
}
327
348
}
328
349
329
- void TComputationPatternLRUCache::NotifyMissing (const TString& serialized) {
330
- TMaybe<TVector<NThreading::TPromise<std::shared_ptr<TPatternCacheEntry>>>> subscribers;
331
- {
332
- std::lock_guard<std::mutex> lock (Mutex);
333
- auto notifyIt = Notify.find (serialized);
334
- if (notifyIt != Notify.end ()) {
335
- subscribers.swap (notifyIt->second );
336
- Notify.erase (notifyIt);
337
- }
338
- }
339
-
340
- if (subscribers) {
341
- for (auto & subscriber : *subscribers) {
342
- subscriber.SetValue (nullptr );
343
- }
344
- }
345
- }
346
-
347
350
} // namespace NKikimr::NMiniKQL
0 commit comments