-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance[MQBSTAT]: lookup for per-appId metrics O(n) -> O(1) #389
Changes from all commits
945bbc2
ff282de
98af0da
588a2c4
f13b4cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -156,6 +156,10 @@ void Cluster::_initializeNetcluster() | |
: k_LEADER_NODE_ID + 1; | ||
dynamic_cast<mqbnet::MockCluster*>(d_netCluster_mp.get()) | ||
->_setSelfNodeId(selfNodeId); | ||
|
||
if (d_isClusterMember) { | ||
BSLS_ASSERT_OPT(0 != d_netCluster_mp->selfNode()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another early safety check for the same problem with dereferencing |
||
} | ||
} | ||
|
||
void Cluster::_initializeNodeSessions() | ||
|
@@ -231,7 +235,12 @@ Cluster::Cluster(bdlbb::BlobBufferFactory* bufferFactory, | |
, d_processor() | ||
{ | ||
// PRECONDITIONS | ||
BSLS_ASSERT_OPT(isClusterMember || !isLeader); | ||
if (isClusterMember) { | ||
BSLS_ASSERT_OPT(!clusterNodeDefs.empty()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Early safety check. Providing an empty |
||
} | ||
else { | ||
BSLS_ASSERT_OPT(!isLeader); | ||
} | ||
|
||
_initializeClusterDefinition(name, | ||
location, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,29 +193,6 @@ bool filterDirect(const mwcst::TableRecords::Record& record) | |
return record.type() == mwcst::StatContext::e_TOTAL_VALUE; | ||
} | ||
|
||
/// Functor object returning `true`, i.e., filter out, if the specified 'name' | ||
/// matches context's name | ||
class ContextNameMatcher { | ||
private: | ||
// DATA | ||
const bsl::string& d_name; | ||
|
||
public: | ||
// CREATORS | ||
ContextNameMatcher(const bsl::string& name) | ||
: d_name(name) | ||
{ | ||
// NOTHING | ||
} | ||
|
||
// ACCESSORS | ||
bool | ||
operator()(const bslma::ManagedPtr<mwcst::StatContext>& context_mp) const | ||
{ | ||
return (context_mp->name() == d_name); | ||
} | ||
}; | ||
|
||
} // close unnamed namespace | ||
|
||
// ----------------------------- | ||
|
@@ -439,21 +416,21 @@ QueueStatsDomain::getValue(const mwcst::StatContext& context, | |
#undef STAT_SINGLE | ||
} | ||
|
||
QueueStatsDomain::QueueStatsDomain() | ||
: d_statContext_mp(0) | ||
, d_subContexts_mp(0) | ||
QueueStatsDomain::QueueStatsDomain(bslma::Allocator* allocator) | ||
: d_allocator_p(bslma::Default::allocator(allocator)) | ||
, d_statContext_mp(0) | ||
, d_subContextsHolder(d_allocator_p) | ||
, d_subContextsLookup(d_allocator_p) | ||
{ | ||
// NOTHING | ||
} | ||
|
||
void QueueStatsDomain::initialize(const bmqt::Uri& uri, | ||
mqbi::Domain* domain, | ||
bslma::Allocator* allocator) | ||
void QueueStatsDomain::initialize(const bmqt::Uri& uri, mqbi::Domain* domain) | ||
{ | ||
BSLS_ASSERT_SAFE(!d_statContext_mp && "initialize was already called"); | ||
|
||
// Create subContext | ||
bdlma::LocalSequentialAllocator<2048> localAllocator(allocator); | ||
bdlma::LocalSequentialAllocator<2048> localAllocator(d_allocator_p); | ||
|
||
d_statContext_mp = domain->queueStatContext()->addSubcontext( | ||
mwcst::StatContextConfiguration(uri.canonical(), &localAllocator)); | ||
|
@@ -491,17 +468,16 @@ void QueueStatsDomain::initialize(const bmqt::Uri& uri, | |
if (!domain->cluster()->isRemote() && | ||
domain->config().mode().isFanoutValue() && | ||
domain->config().mode().fanout().publishAppIdMetrics()) { | ||
d_subContexts_mp.load(new (*allocator) | ||
bsl::list<StatSubContextMp>(allocator), | ||
allocator); | ||
const bsl::vector<bsl::string>& appIDs = | ||
domain->config().mode().fanout().appIDs(); | ||
for (bsl::vector<bsl::string>::const_iterator cit = appIDs.begin(); | ||
cit != appIDs.end(); | ||
++cit) { | ||
StatSubContextMp subContext = d_statContext_mp->addSubcontext( | ||
mwcst::StatContextConfiguration(*cit, &localAllocator)); | ||
d_subContexts_mp->emplace_back( | ||
|
||
d_subContextsLookup.insert(bsl::make_pair(*cit, subContext.get())); | ||
d_subContextsHolder.emplace_back( | ||
bslmf::MovableRefUtil::move(subContext)); | ||
} | ||
} | ||
|
@@ -609,25 +585,27 @@ void QueueStatsDomain::onEvent(EventType::Enum type, | |
|
||
BALL_LOG_SET_CATEGORY(k_LOG_CATEGORY); | ||
|
||
if (!d_subContexts_mp) { | ||
if (d_subContextsLookup.empty()) { | ||
BALL_LOGTHROTTLE_WARN(k_MAX_INSTANT_MESSAGES, k_NS_PER_MESSAGE) | ||
<< "[THROTTLED] No built sub contexts"; | ||
<< "[THROTTLED] No built sub contexts for domain: " | ||
<< d_statContext_mp->name() << ", appId: " << appId; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New log line looks like |
||
return; // RETURN | ||
} | ||
|
||
bsl::list<StatSubContextMp>::iterator it = bsl::find_if( | ||
d_subContexts_mp->begin(), | ||
d_subContexts_mp->end(), | ||
ContextNameMatcher(appId)); | ||
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(it == d_subContexts_mp->end())) { | ||
bsl::unordered_map<bsl::string, mwcst::StatContext*>::iterator it = | ||
d_subContextsLookup.find(appId); | ||
|
||
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(it == | ||
d_subContextsLookup.end())) { | ||
BSLS_PERFORMANCEHINT_UNLIKELY_HINT; | ||
|
||
BALL_LOGTHROTTLE_WARN(k_MAX_INSTANT_MESSAGES, k_NS_PER_MESSAGE) | ||
<< "[THROTTLED] No matching StatContext for appId: " << appId; | ||
<< "[THROTTLED] No matching StatContext for domain: " | ||
<< d_statContext_mp->name() << ", appId: " << appId; | ||
return; // RETURN | ||
} | ||
|
||
mwcst::StatContext* appIdContext = it->get(); | ||
mwcst::StatContext* appIdContext = it->second; | ||
BSLS_ASSERT_SAFE(appIdContext); | ||
|
||
switch (type) { | ||
|
@@ -678,38 +656,54 @@ void QueueStatsDomain::setQueueContentRaw(bsls::Types::Int64 messages, | |
void QueueStatsDomain::updateDomainAppIds( | ||
const bsl::vector<bsl::string>& appIds) | ||
{ | ||
if (!d_subContexts_mp) { | ||
if (appIds.empty()) { | ||
d_subContextsLookup.clear(); | ||
d_subContextsHolder.clear(); | ||
return; // RETURN | ||
} | ||
|
||
bdlma::LocalSequentialAllocator<2048> localAllocator; | ||
|
||
// Add subcontexts for appIds that are not already present | ||
for (bsl::vector<bsl::string>::const_iterator cit = appIds.begin(); | ||
cit != appIds.end(); | ||
++cit) { | ||
if (bsl::find_if(d_subContexts_mp->begin(), | ||
d_subContexts_mp->end(), | ||
ContextNameMatcher(*cit)) == | ||
d_subContexts_mp->end()) { | ||
StatSubContextMp subContext = d_statContext_mp->addSubcontext( | ||
mwcst::StatContextConfiguration(*cit, &localAllocator)); | ||
d_subContexts_mp->emplace_back( | ||
bslmf::MovableRefUtil::move(subContext)); | ||
} | ||
} | ||
|
||
// Remove subcontexts if appIds are not present in updated AppIds | ||
bsl::list<StatSubContextMp>::iterator it = d_subContexts_mp->begin(); | ||
while (it != d_subContexts_mp->end()) { | ||
if (bsl::find(appIds.begin(), appIds.end(), it->get()->name()) == | ||
appIds.end()) { | ||
it = d_subContexts_mp->erase(it); | ||
bsl::unordered_set<bsl::string> remainingAppIds(appIds.begin(), | ||
appIds.end(), | ||
d_allocator_p); | ||
|
||
// 1. Remove subcontexts for unneeded appIds | ||
bsl::list<StatSubContextMp>::iterator it = d_subContextsHolder.begin(); | ||
while (it != d_subContextsHolder.end()) { | ||
const bsl::string& ctxAppId = it->get()->name(); | ||
bsl::unordered_set<bsl::string>::const_iterator sIt = | ||
remainingAppIds.find(ctxAppId); | ||
if (sIt == remainingAppIds.end()) { | ||
// Subcontext for this appId is no longer needed, remove it from | ||
// the holder and lookup table | ||
d_subContextsLookup.erase(ctxAppId); | ||
it = d_subContextsHolder.erase(it); | ||
} | ||
else { | ||
// This appId is needed, but the stat context is already built for | ||
// it | ||
remainingAppIds.erase(sIt); | ||
++it; | ||
} | ||
} | ||
|
||
if (remainingAppIds.empty()) { | ||
return; // RETURN | ||
} | ||
|
||
// 2. Add the remaining appIds | ||
bdlma::LocalSequentialAllocator<2048> localAllocator(d_allocator_p); | ||
|
||
for (bsl::unordered_set<bsl::string>::const_iterator sIt = | ||
remainingAppIds.begin(); | ||
sIt != remainingAppIds.end(); | ||
sIt++) { | ||
StatSubContextMp subContext = d_statContext_mp->addSubcontext( | ||
mwcst::StatContextConfiguration(*sIt, &localAllocator)); | ||
|
||
d_subContextsLookup.insert(bsl::make_pair(*sIt, subContext.get())); | ||
d_subContextsHolder.emplace_back( | ||
bslmf::MovableRefUtil::move(subContext)); | ||
} | ||
} | ||
|
||
// ----------------------------- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the line 65 below,
netCluster->selfNode()
is dereferenced without checks, which might lead to segfault. This might be achieved by using mock domain/cluster objects.