2121#include < daemon/mcbp.h>
2222#include < daemon/buckets.h>
2323
24+ /* *
25+ * Get the timing histogram for the specified bucket if we've got access
26+ * to the bucket.
27+ *
28+ * @param connection The connection executing the command
29+ * @param bucket The bucket to get the timing data from
30+ * @param opcode The opcode to get the timing histogram for
31+ * @return A std::pair with the first being the error code for the operation
32+ * and the second being the histogram (only valid if the first
33+ * parameter is ENGINE_SUCCESS)
34+ */
35+ static std::pair<ENGINE_ERROR_CODE, TimingHistogram> get_timings (
36+ McbpConnection& connection, const Bucket& bucket, uint8_t opcode) {
37+ // Don't creata a new privilege context if the one we've got is for the
38+ // connected bucket:
39+ if (bucket.name == connection.getBucket ().name ) {
40+ auto ret = mcbp::checkPrivilege (connection,
41+ cb::rbac::Privilege::SimpleStats);
42+ if (ret != ENGINE_SUCCESS) {
43+ return std::make_pair (ENGINE_EACCESS, TimingHistogram{});
44+ }
45+ } else {
46+ // Check to see if we've got access to the bucket
47+ bool access = false ;
48+ try {
49+ auto context = cb::rbac::createContext (connection.getUsername (),
50+ bucket.name );
51+ const auto check = context.check (cb::rbac::Privilege::SimpleStats);
52+ if (check == cb::rbac::PrivilegeAccess::Ok) {
53+ access = true ;
54+ }
55+ } catch (const cb::rbac::Exception& e) {
56+ // We don't have access to that bucket
57+ }
58+
59+ if (!access) {
60+ return std::make_pair (ENGINE_EACCESS, TimingHistogram{});
61+ }
62+ }
63+
64+ return std::make_pair (ENGINE_SUCCESS,
65+ bucket.timings .get_timing_histogram (opcode));
66+ }
67+
68+ /* *
69+ * Get the command timings for the provided bucket if:
70+ * it's not NoBucket
71+ * it is running
72+ * it has the given name
73+ *
74+ * @param connection The connection requesting access
75+ * @param bucket The bucket to look at
76+ * @param opcode The opcode we're interested in
77+ * @param bucketname The name of the bucket we want
78+ */
79+ static std::pair<ENGINE_ERROR_CODE, TimingHistogram> maybe_get_timings (
80+ McbpConnection& connection, const Bucket& bucket, uint8_t opcode, const std::string& bucketname) {
81+
82+ std::pair<ENGINE_ERROR_CODE, TimingHistogram> ret = std::make_pair (ENGINE_KEY_ENOENT, TimingHistogram{});
83+
84+ cb_mutex_enter (&bucket.mutex );
85+ try {
86+ if (bucket.type != BucketType::NoBucket &&
87+ bucket.state == BucketState::Ready && bucketname == bucket.name ) {
88+ ret = get_timings (connection, bucket, opcode);
89+ }
90+ } catch (...) {
91+ // we don't want to leave the mutex locked
92+ }
93+ cb_mutex_exit (&bucket.mutex );
94+
95+ return ret;
96+ }
97+
98+ /* *
99+ * Get the aggregated timings across "all" buckets that the connected
100+ * client has access to.
101+ */
102+ static std::pair<ENGINE_ERROR_CODE, std::string> get_aggregated_timings (
103+ McbpConnection& connection, uint8_t opcode) {
104+ TimingHistogram timings;
105+ bool found = false ;
106+
107+ for (auto & bucket : all_buckets) {
108+ auto bt = maybe_get_timings (connection, bucket, opcode, bucket.name );
109+ if (bt.first == ENGINE_SUCCESS) {
110+ timings += bt.second ;
111+ found = true ;
112+ }
113+ }
114+
115+ if (found) {
116+ return std::make_pair (ENGINE_SUCCESS, timings.to_string ());
117+ }
118+
119+ // We didn't have access to any buckets!
120+ return std::make_pair (ENGINE_EACCESS, std::string{});
121+ }
122+
24123std::pair<ENGINE_ERROR_CODE, std::string> get_cmd_timer (
25124 McbpConnection& connection,
26125 const protocol_binary_request_get_cmd_timer* req) {
@@ -30,52 +129,44 @@ std::pair<ENGINE_ERROR_CODE, std::string> get_cmd_timer(
30129 const std::string bucket (key, keylen);
31130 const auto opcode = req->message .body .opcode ;
32131
33- if (keylen > 0 && bucket != all_buckets[index].name ) {
34- // The user specified the current selected bucket
35- keylen = 0 ;
132+ if (bucket == " /all/" ) {
133+ index = 0 ;
36134 }
37135
38- if (keylen > 0 || index == 0 ) {
39- // You need the Stats privilege in order to specify a bucket
40- auto ret = mcbp::checkPrivilege (connection, cb::rbac::Privilege::Stats);
41- if (ret != ENGINE_SUCCESS) {
42- return std::make_pair (ret, " " );
43- }
136+ if (index == 0 ) {
137+ return get_aggregated_timings (connection, opcode);
44138 }
45139
46- // At this point we know that the user have the appropriate access
47- // and should be permitted to perform the action
48- if (bucket == " /all/" ) {
49- // The aggregated timings is stored in index 0 (no bucket)
50- index = 0 ;
51- keylen = 0 ;
52- }
140+ if (bucket.empty () || bucket == all_buckets[index].name ) {
141+ // The current selected bucket
142+ auto bt = get_timings (connection, connection.getBucket (), opcode);
143+ if (bt.first == ENGINE_SUCCESS) {
144+ return std::make_pair (ENGINE_SUCCESS, bt.second .to_string ());
145+ }
53146
54- if (keylen == 0 ) {
55- return std::make_pair (ENGINE_SUCCESS,
56- all_buckets[index].timings .generate (opcode));
147+ return std::make_pair (bt.first , std::string{});
57148 }
58149
59150 // The user specified a bucket... let's locate the bucket
60- std::string str ;
151+ std::pair<ENGINE_ERROR_CODE, TimingHistogram> ret ;
61152
62- bool found = false ;
63- for (size_t ii = 1 ; ii < all_buckets.size () && !found; ++ii) {
64- // Need the lock to get the bucket state and name
65- cb_mutex_enter (&all_buckets[ii].mutex );
66- if ((all_buckets[ii].state == BucketState::Ready) &&
67- (bucket == all_buckets[ii].name )) {
68- str = all_buckets[ii].timings .generate (opcode);
69- found = true ;
153+ for (auto & b : all_buckets) {
154+ ret = maybe_get_timings (connection, b, opcode, bucket);
155+ if (ret.first != ENGINE_KEY_ENOENT && ret.first != ENGINE_SUCCESS) {
156+ break ;
70157 }
71- cb_mutex_exit (&all_buckets[ii].mutex );
72158 }
73159
74- if (found) {
75- return std::make_pair (ENGINE_SUCCESS, str);
160+ if (ret.first == ENGINE_SUCCESS) {
161+ return std::make_pair (ENGINE_SUCCESS, ret.second .to_string ());
162+ }
163+
164+ if (ret.first == ENGINE_KEY_ENOENT) {
165+ // Don't tell the user that the bucket doesn't exist
166+ ret.first = ENGINE_EACCESS;
76167 }
77168
78- return std::make_pair (ENGINE_KEY_ENOENT, " " );
169+ return std::make_pair (ret. first , std::string{} );
79170}
80171
81172void get_cmd_timer_executor (McbpConnection* c, void * packet) {
0 commit comments