Skip to content

Commit 7ebb29e

Browse files
yfeldblumfacebook-github-bot
authored andcommitted
let ServiceData::getRegexCountersOptimized use folly::RegexMatchCache
Reviewed By: mdas7 Differential Revision: D54708756 fbshipit-source-id: e849f26017efb8827b014233dda087cebb06cadd
1 parent c891ff5 commit 7ebb29e

11 files changed

+143
-205
lines changed

fb303/BUCK

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ cpp_library(
3737
"//folly:range",
3838
"//folly:synchronized",
3939
"//folly/container:f14_hash",
40+
"//folly/container:regex_match_cache",
4041
"//folly/container:reserve",
4142
"//folly/synchronization:relaxed_atomic",
4243
],
@@ -196,6 +197,7 @@ cpp_library(
196197
"//folly:indestructible",
197198
"//folly:map_util",
198199
"//folly:string",
200+
"//folly/container:reserve",
199201
],
200202
exported_deps = [
201203
":dynamic_counters",
@@ -207,6 +209,7 @@ cpp_library(
207209
"//folly:range",
208210
"//folly:synchronized",
209211
"//folly/container:f14_hash",
212+
"//folly/container:regex_match_cache",
210213
"//folly/synchronization:relaxed_atomic",
211214
],
212215
external_deps = [

fb303/CallbackValuesMap-inl.h

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ void CallbackValuesMap<T>::getKeys(std::vector<std::string>* keys) const {
7777
template <typename T>
7878
void CallbackValuesMap<T>::getRegexKeys(
7979
std::vector<std::string>& keys,
80-
const std::string& regex) const {
81-
detail::getRegexKeysImpl(keys, regex, callbackMap_);
80+
const std::string& regex,
81+
const folly::RegexMatchCache::time_point now) const {
82+
detail::cachedFindMatches(keys, callbackMap_, regex, now);
8283
}
8384

8485
template <typename T>
@@ -91,11 +92,9 @@ void CallbackValuesMap<T>::registerCallback(
9192
folly::StringPiece name,
9293
const Callback& cob) {
9394
auto wlock = callbackMap_.wlock();
94-
wlock->map[std::string(name)] = std::make_shared<CallbackEntry>(cob);
95+
auto iter = detail::cachedAddString(*wlock, name, nullptr);
9596

96-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
97-
uint64_t epoch = wlock->mapEpoch.load();
98-
wlock->mapEpoch.store(epoch + 1);
97+
iter->second = std::make_shared<CallbackEntry>(cob);
9998
}
10099

101100
template <typename T>
@@ -107,11 +106,7 @@ bool CallbackValuesMap<T>::unregisterCallback(folly::StringPiece name) {
107106
}
108107
auto callbackCopy = std::move(entry->second);
109108

110-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
111-
uint64_t epoch = wlock->mapEpoch.load();
112-
wlock->mapEpoch.store(epoch + 1);
113-
114-
wlock->map.erase(entry);
109+
detail::cachedEraseString(*wlock, entry);
115110
VLOG(5) << "Unregistered callback: " << name;
116111

117112
// clear the callback after releasing the lock
@@ -126,10 +121,7 @@ void CallbackValuesMap<T>::clear() {
126121
for (auto& entry : wlock->map) {
127122
entry.second->clear();
128123
}
129-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
130-
uint64_t epoch = wlock->mapEpoch.load();
131-
wlock->mapEpoch.store(epoch + 1);
132-
wlock->map.clear();
124+
detail::cachedClearStrings(*wlock);
133125
}
134126

135127
template <typename T>

fb303/CallbackValuesMap.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <folly/Range.h>
2525
#include <folly/Synchronized.h>
2626
#include <folly/container/F14Map.h>
27+
#include <folly/container/RegexMatchCache.h>
2728
#include <folly/synchronization/RelaxedAtomic.h>
2829

2930
namespace facebook {
@@ -57,7 +58,14 @@ class CallbackValuesMap {
5758

5859
/* Returns the keys in the map that matches regex pattern */
5960
void getRegexKeys(std::vector<std::string>& keys, const std::string& regex)
60-
const;
61+
const {
62+
const auto now = folly::RegexMatchCache::clock::now();
63+
getRegexKeys(keys, regex, now);
64+
}
65+
void getRegexKeys(
66+
std::vector<std::string>& keys,
67+
const std::string& regex,
68+
const folly::RegexMatchCache::time_point now) const;
6169

6270
/** Returns the number of keys present in the map */
6371
size_t getNumKeys() const;
@@ -83,6 +91,10 @@ class CallbackValuesMap {
8391
*/
8492
void clear();
8593

94+
void trimRegexCache(folly::RegexMatchCache::time_point expiry) {
95+
callbackMap_.wlock()->matches.purge(expiry);
96+
}
97+
8698
class CallbackEntry {
8799
public:
88100
explicit CallbackEntry(const Callback& callback);
@@ -107,12 +119,8 @@ class CallbackValuesMap {
107119
// match, cache is valid.
108120
template <typename Mapped>
109121
struct MapWithKeyCache {
110-
folly::F14FastMap<std::string, Mapped> map;
111-
mutable folly::F14FastMap<std::string, std::vector<std::string>> regexCache;
112-
mutable folly::relaxed_atomic_uint64_t mapEpoch{0};
113-
mutable folly::relaxed_atomic_uint64_t cacheEpoch{0};
114-
mutable folly::chrono::coarse_system_clock::time_point cacheClearTime{
115-
std::chrono::seconds(0)};
122+
folly::F14NodeMap<std::string, Mapped> map;
123+
folly::RegexMatchCache matches; // requires map to have reference stability
116124
};
117125

118126
using CallbackMap = MapWithKeyCache<std::shared_ptr<CallbackEntry>>;

fb303/ServiceData.cpp

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <folly/Indestructible.h>
2626
#include <folly/MapUtil.h>
2727
#include <folly/String.h>
28+
#include <folly/container/Reserve.h>
2829
#include <gflags/gflags.h>
2930

3031
using folly::StringPiece;
@@ -76,14 +77,7 @@ void ServiceData::flushAllData() {
7677

7778
void ServiceData::resetAllData() {
7879
options_.wlock()->clear();
79-
{
80-
auto countersWLock = counters_.wlock();
81-
countersWLock->map.clear();
82-
83-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
84-
uint64_t epoch = countersWLock->mapEpoch.load();
85-
countersWLock->mapEpoch.store(epoch + 1);
86-
}
80+
detail::cachedClearStrings(*counters_.wlock());
8781
exportedValues_.wlock()->clear();
8882

8983
statsMap_.forgetAllStats();
@@ -317,11 +311,7 @@ int64_t ServiceData::incrementCounter(StringPiece key, int64_t amount) {
317311

318312
// pessimistically, the key is possibly absent; upsert under wlock
319313
auto countersWLock = counters_.wlock();
320-
auto& ref = (countersWLock->map)[std::string(key)];
321-
322-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
323-
uint64_t epoch = countersWLock->mapEpoch.load();
324-
countersWLock->mapEpoch.store(epoch + 1);
314+
auto& ref = detail::cachedAddString(*countersWLock, key, 0)->second;
325315

326316
return ref.fetch_add(amount, std::memory_order_relaxed) + amount;
327317
}
@@ -340,24 +330,16 @@ int64_t ServiceData::setCounter(StringPiece key, int64_t value) {
340330

341331
// pessimistically, the key is possibly absent; upsert under wlock
342332
auto countersWLock = counters_.wlock();
343-
auto& ref = (countersWLock->map)[std::string(key)];
344-
345-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
346-
uint64_t epoch = countersWLock->mapEpoch.load();
347-
countersWLock->mapEpoch.store(epoch + 1);
333+
auto& ref = detail::cachedAddString(*countersWLock, key, 0)->second;
348334

349335
ref.store(value, std::memory_order_relaxed);
350336
return value;
351337
}
352338

353339
void ServiceData::clearCounter(StringPiece key) {
354340
auto countersWLock = counters_.wlock();
355-
auto it = countersWLock->map.find(key);
356-
if (it != countersWLock->map.end()) {
357-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
358-
uint64_t epoch = countersWLock->mapEpoch.load();
359-
countersWLock->mapEpoch.store(epoch + 1);
360-
countersWLock->map.erase(it);
341+
if (auto it = countersWLock->map.find(key); it != countersWLock->map.end()) {
342+
detail::cachedEraseString(*countersWLock, it);
361343
}
362344
}
363345

@@ -494,10 +476,11 @@ void ServiceData::getRegexCounters(
494476
void ServiceData::getRegexCountersOptimized(
495477
map<string, int64_t>& output,
496478
const string& regex) const {
479+
const auto now = folly::RegexMatchCache::clock::now();
497480
std::vector<std::string> keys;
498-
detail::getRegexKeysImpl(keys, regex, counters_);
499-
quantileMap_.getRegexKeys(keys, regex);
500-
dynamicCounters_.getRegexKeys(keys, regex);
481+
detail::cachedFindMatches(keys, counters_, regex, now);
482+
quantileMap_.getRegexKeys(keys, regex, now);
483+
dynamicCounters_.getRegexKeys(keys, regex, now);
501484
getSelectedCounters(output, keys);
502485
}
503486

@@ -514,6 +497,14 @@ map<string, int64_t> ServiceData::getRegexCountersOptimized(
514497
return output;
515498
}
516499

500+
void ServiceData::trimRegexCache(const std::chrono::seconds maxstale) {
501+
const auto now = folly::RegexMatchCache::clock::now();
502+
const auto expiry = now - maxstale;
503+
counters_.wlock()->matches.purge(expiry);
504+
quantileMap_.trimRegexCache(expiry);
505+
dynamicCounters_.trimRegexCache(expiry);
506+
}
507+
517508
bool ServiceData::hasCounter(StringPiece key) const {
518509
if (dynamicCounters_.contains(key)) {
519510
return true;

fb303/ServiceData.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <folly/Range.h>
2727
#include <folly/Synchronized.h>
2828
#include <folly/container/F14Map.h>
29+
#include <folly/container/RegexMatchCache.h>
2930
#include <folly/synchronization/RelaxedAtomic.h>
3031

3132
#include <atomic>
@@ -473,6 +474,7 @@ class ServiceData {
473474
const std::string& regex) const;
474475
std::map<std::string, int64_t> getRegexCountersOptimized(
475476
const std::string& regex) const;
477+
void trimRegexCache(std::chrono::seconds maxstale);
476478
/*** Returns true if a counter exists with the specified name */
477479
bool hasCounter(folly::StringPiece key) const;
478480

@@ -570,7 +572,8 @@ class ServiceData {
570572

571573
private:
572574
struct Counter : std::atomic<int64_t> {
573-
Counter() : std::atomic<int64_t>{0} {}
575+
Counter() noexcept : std::atomic<int64_t>{0} {}
576+
explicit Counter(int64_t v) noexcept : std::atomic<int64_t>{v} {}
574577
Counter(Counter&& other) noexcept
575578
: std::atomic<int64_t>{other.load(std::memory_order_relaxed)} {}
576579
};
@@ -588,11 +591,7 @@ class ServiceData {
588591
template <typename Mapped>
589592
struct MapWithKeyCache {
590593
std::map<std::string, Mapped, std::less<>> map;
591-
mutable folly::F14FastMap<std::string, std::vector<std::string>> regexCache;
592-
mutable folly::relaxed_atomic_uint64_t mapEpoch{0};
593-
mutable folly::relaxed_atomic_uint64_t cacheEpoch{0};
594-
mutable folly::chrono::coarse_system_clock::time_point cacheClearTime{
595-
std::chrono::seconds(0)};
594+
folly::RegexMatchCache matches; // requires map to have reference stability
596595
};
597596
folly::Synchronized<MapWithKeyCache<Counter>> counters_;
598597

fb303/ThreadCachedServiceData.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
#include <folly/Indestructible.h>
2020
#include <folly/Singleton.h>
2121

22+
using namespace std::literals;
23+
2224
using std::chrono::milliseconds;
2325

2426
namespace {
2527
static const std::string kFunctionId =
2628
"ThreadCachedStatsMap::aggregateAcrossAllThreads";
29+
static const std::string kPurgeFunctionId = "ServiceData::trimRegexCache";
30+
static const auto kPurgeInterval = 600s;
31+
static const auto kPurgeMaxStale = 3600s;
2732
} // namespace
2833

2934
namespace facebook::fb303 {
@@ -41,6 +46,14 @@ class PublisherManager {
4146
[] { ThreadCachedServiceData::getInternal().publishStats(); },
4247
ThreadCachedServiceData::getInternal().getPublisherInterval(),
4348
kFunctionId);
49+
fs_.addFunction(
50+
[] {
51+
ThreadCachedServiceData::getInternal()
52+
.getServiceData()
53+
->trimRegexCache(kPurgeMaxStale);
54+
},
55+
kPurgeInterval,
56+
kPurgeFunctionId);
4457
fs_.setThreadName("servicedata-pub");
4558
fs_.start();
4659
}

fb303/detail/BUCK

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ cpp_library(
2222
"//folly:optional",
2323
"//folly:synchronized",
2424
"//folly/container:f14_hash",
25+
"//folly/container:regex_match_cache",
2526
"//folly/synchronization:relaxed_atomic",
2627
],
2728
exported_external_deps = [
@@ -60,6 +61,8 @@ cpp_library(
6061
"//folly:chrono",
6162
"//folly:map_util",
6263
"//folly/container:f14_hash",
64+
"//folly/container:regex_match_cache",
65+
"//folly/container:reserve",
6366
],
6467
external_deps = [
6568
("boost", None, "boost_regex"),

fb303/detail/QuantileStatMap-inl.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,9 @@ void BasicQuantileStatMap<ClockT>::getKeys(
179179
template <typename ClockT>
180180
void BasicQuantileStatMap<ClockT>::getRegexKeys(
181181
std::vector<std::string>& keys,
182-
const std::string& regex) const {
183-
getRegexKeysImpl(keys, regex, counters_);
182+
const std::string& regex,
183+
const folly::RegexMatchCache::time_point now) const {
184+
detail::cachedFindMatches(keys, counters_, regex, now);
184185
}
185186

186187
template <typename ClockT>
@@ -221,19 +222,16 @@ BasicQuantileStatMap<ClockT>::registerQuantileStat(
221222
CounterMapEntry entry;
222223
entry.stat = stat;
223224
entry.statDef = statDef;
224-
countersWLock->map.emplace(makeKey(name, statDef, folly::none), entry);
225+
detail::cachedAddString(
226+
*countersWLock, makeKey(name, statDef, folly::none), entry);
225227

226228
auto slidingWindowLengths = stat->getSlidingWindowLengths();
227229

228230
for (auto slidingWindowLength : slidingWindowLengths) {
229231
entry.slidingWindowLength = slidingWindowLength;
230-
countersWLock->map.emplace(
231-
makeKey(name, statDef, slidingWindowLength), entry);
232+
detail::cachedAddString(
233+
*countersWLock, makeKey(name, statDef, slidingWindowLength), entry);
232234
}
233-
234-
// avoid fetch_add() to avoid extra fences, since we hold the lock already
235-
uint64_t epoch = countersWLock->mapEpoch.load();
236-
countersWLock->mapEpoch.store(epoch + 1);
237235
}
238236
StatMapEntry statMapEntry;
239237
statMapEntry.stat = stat;

0 commit comments

Comments
 (0)