1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #pragma once
18 
19 #include <cstdint>
20 #include <relaxed_atomic.h>
21 #include <mutex>
22 
23 #include "timing_histogram.h"
24 
25 /**
26  * Stats stored per-thread.
27  */
28 struct thread_stats {
thread_statsthread_stats29     thread_stats() {
30         reset();
31     }
32 
resetthread_stats33     void reset() {
34         cmd_get = 0;
35         get_hits = 0;
36         get_misses = 0;
37         cmd_set = 0;
38         delete_hits = 0;
39         cas_hits = 0;
40         cas_badval = 0;
41         delete_misses = 0;
42         incr_misses = 0;
43         decr_misses = 0;
44         incr_hits = 0;
45         decr_hits = 0;
46         cas_misses = 0;
47         bytes_written = 0;
48         bytes_read = 0;
49         cmd_flush = 0;
50         conn_yields = 0;
51         auth_cmds = 0;
52         auth_errors = 0;
53         cmd_subdoc_lookup = 0;
54         cmd_subdoc_mutation = 0;
55         cmd_lock = 0;
56         lock_errors = 0;
57 
58         bytes_subdoc_lookup_total = 0;
59         bytes_subdoc_lookup_extracted = 0;
60         bytes_subdoc_mutation_total = 0;
61         bytes_subdoc_mutation_inserted = 0;
62 
63         rbufs_allocated = 0;
64         rbufs_loaned = 0;
65         rbufs_existing = 0;
66         wbufs_allocated = 0;
67         wbufs_loaned = 0;
68         wbufs_existing = 0;
69 
70         iovused_high_watermark = 0;
71         msgused_high_watermark = 0;
72     }
73 
operator +=thread_stats74     thread_stats & operator += (const thread_stats &other) {
75         cmd_get += other.cmd_get;
76         get_misses += other.get_misses;
77         cmd_set += other.cmd_set;
78         get_hits += other.get_hits;
79         delete_hits += other.delete_hits;
80         cas_hits += other.cas_hits;
81         cas_badval += other.cas_badval;
82         delete_misses += other.delete_misses;
83         decr_misses += other.decr_misses;
84         incr_misses += other.incr_misses;
85         decr_hits += other.decr_hits;
86         incr_hits += other.incr_hits;
87         cas_misses += other.cas_misses;
88         bytes_read += other.bytes_read;
89         bytes_written += other.bytes_written;
90         cmd_flush += other.cmd_flush;
91         conn_yields += other.conn_yields;
92         auth_cmds += other.auth_cmds;
93         auth_errors += other.auth_errors;
94         cmd_subdoc_lookup += other.cmd_subdoc_lookup;
95         cmd_subdoc_mutation += other.cmd_subdoc_mutation;
96 
97         cmd_lock += other.cmd_lock;
98         lock_errors += other.lock_errors;
99 
100         bytes_subdoc_lookup_total += other.bytes_subdoc_lookup_total;
101         bytes_subdoc_lookup_extracted += other.bytes_subdoc_lookup_extracted;
102         bytes_subdoc_mutation_total += other.bytes_subdoc_mutation_total;
103         bytes_subdoc_mutation_inserted += other.bytes_subdoc_mutation_inserted;
104 
105         rbufs_allocated += other.rbufs_allocated;
106         rbufs_loaned += other.rbufs_loaned;
107         rbufs_existing += other.rbufs_existing;
108         wbufs_allocated += other.wbufs_allocated;
109         wbufs_loaned += other.wbufs_loaned;
110         wbufs_existing += other.wbufs_existing;
111 
112         iovused_high_watermark.setIfGreater(other.iovused_high_watermark);
113         msgused_high_watermark.setIfGreater(other.msgused_high_watermark);
114 
115         return *this;
116     }
117 
aggregatethread_stats118     void aggregate(const std::vector<thread_stats>& thread_stats) {
119         for (auto& ii : thread_stats) {
120             *this += ii;
121         }
122     }
123 
124     Couchbase::RelaxedAtomic<uint64_t> cmd_get;
125     Couchbase::RelaxedAtomic<uint64_t> get_hits;
126     Couchbase::RelaxedAtomic<uint64_t> get_misses;
127     Couchbase::RelaxedAtomic<uint64_t> cmd_set;
128     Couchbase::RelaxedAtomic<uint64_t> delete_hits;
129     Couchbase::RelaxedAtomic<uint64_t> cas_hits;
130     Couchbase::RelaxedAtomic<uint64_t> cas_badval;
131     Couchbase::RelaxedAtomic<uint64_t> delete_misses;
132     Couchbase::RelaxedAtomic<uint64_t> incr_misses;
133     Couchbase::RelaxedAtomic<uint64_t> decr_misses;
134     Couchbase::RelaxedAtomic<uint64_t> incr_hits;
135     Couchbase::RelaxedAtomic<uint64_t> decr_hits;
136     Couchbase::RelaxedAtomic<uint64_t> cas_misses;
137     Couchbase::RelaxedAtomic<uint64_t> bytes_read;
138     Couchbase::RelaxedAtomic<uint64_t> bytes_written;
139     Couchbase::RelaxedAtomic<uint64_t> cmd_flush;
140     Couchbase::RelaxedAtomic<uint64_t> conn_yields; /* # of yields for connections (-R option)*/
141     Couchbase::RelaxedAtomic<uint64_t> auth_cmds;
142     Couchbase::RelaxedAtomic<uint64_t> auth_errors;
143     /* # of subdoc lookup commands (GET/EXISTS/MULTI_LOOKUP) */
144     Couchbase::RelaxedAtomic<uint64_t> cmd_subdoc_lookup;
145     /* # of subdoc mutation commands */
146     Couchbase::RelaxedAtomic<uint64_t> cmd_subdoc_mutation;
147 
148     /** # of lock commands */
149     Couchbase::RelaxedAtomic<uint64_t> cmd_lock;
150 
151     /** # of times an operation failed due to accessing a locked item */
152     Couchbase::RelaxedAtomic<uint64_t> lock_errors;
153 
154     /* # of bytes in the complete document which subdoc lookups searched
155        within. Compare with 'bytes_subdoc_lookup_extracted' */
156     Couchbase::RelaxedAtomic<uint64_t> bytes_subdoc_lookup_total;
157     /* # of bytes extracted during a subdoc lookup operation and sent back to
158       the client. */
159     Couchbase::RelaxedAtomic<uint64_t> bytes_subdoc_lookup_extracted;
160 
161     /* # of bytes in the complete document which subdoc mutations updated.
162        Compare with 'bytes_subdoc_mutation_inserted' */
163     Couchbase::RelaxedAtomic<uint64_t> bytes_subdoc_mutation_total;
164     /* # of bytes inserted during a subdoc mutation operation (which were
165        received from the client). */
166     Couchbase::RelaxedAtomic<uint64_t> bytes_subdoc_mutation_inserted;
167 
168     /* # of read buffers allocated. */
169     Couchbase::RelaxedAtomic<uint64_t> rbufs_allocated;
170     /* # of read buffers which could be loaned (and hence didn't need to be allocated). */
171     Couchbase::RelaxedAtomic<uint64_t> rbufs_loaned;
172     /* # of read buffers which already existed (with partial data) on the connection
173        (and hence didn't need to be allocated). */
174     Couchbase::RelaxedAtomic<uint64_t> rbufs_existing;
175     /* # of write buffers allocated. */
176     Couchbase::RelaxedAtomic<uint64_t> wbufs_allocated;
177     /* # of write buffers which could be loaned (and hence didn't need to be allocated). */
178     Couchbase::RelaxedAtomic<uint64_t> wbufs_loaned;
179     /* # of write buffers which already existed (with partial data) on the
180         connection (and hence didn't need to be allocated). */
181     Couchbase::RelaxedAtomic<uint64_t> wbufs_existing;
182 
183     /* Highest value iovsize has got to */
184     Couchbase::RelaxedAtomic<int> iovused_high_watermark;
185     /* High value Connection->msgused has got to */
186     Couchbase::RelaxedAtomic<int> msgused_high_watermark;
187 };
188 
189 /**
190  * A class representing the properties used by Listening port.
191  *
192  * This class differs from the "interface" class that it represents
193  * an actual port memcached have open. It contains some dynamic and
194  * some fixed properties
195  */
196 class ListeningPort {
197 public:
ListeningPort(const in_port_t port_, const std::string& host_, bool tcp_nodelay_, int backlog_, bool management_)198     ListeningPort(const in_port_t port_,
199                   const std::string& host_,
200                   bool tcp_nodelay_,
201                   int backlog_,
202                   bool management_)
203         : port(port_),
204           curr_conns(1),
205           maxconns(0),
206           host(host_),
207           backlog(backlog_),
208           ipv6(false),
209           ipv4(false),
210           tcp_nodelay(tcp_nodelay_),
211           management(management_) {
212     }
213 
214     /**
215      * The actual port number being used by this connection. Please note
216      * that you cannot configure the system to use the same port, but different
217      * hostnames.
218      */
219     const in_port_t port;
220 
221     /** The current number of connections connected to this port */
222     int curr_conns;
223 
224     /** The maximum number of connections allowed for this port */
225     int maxconns;
226 
227     /** The hostname this port is bound to ("*" means all interfaces) */
228     const std::string host;
229 
230     /** SSL related properties for the port */
231     struct ifc_ssl_info {
ifc_ssl_infoListeningPort::ifc_ssl_info232         ifc_ssl_info()
233             : enabled(false) {
234         }
235 
ifc_ssl_infoListeningPort::ifc_ssl_info236         ifc_ssl_info(const ifc_ssl_info& o)
237             : enabled(o.enabled),
238               key(o.key),
239               cert(o.cert) {}
240 
241         /** Is ssl enabled or not */
242         bool enabled;
243         /** The name of the file containing the SSL key */
244         std::string key;
245         /** The name of the file containing the certificate */
246         std::string cert;
247     } ssl;
248 
249     /** The backlog size before the kernel will deny connect requests */
250     int backlog;
251     /** Is IPv6 enabled for this port */
252     bool ipv6;
253     /** Is IPv4 enabled for this port */
254     bool ipv4;
255     /** Should TCP_NODELAY be enabled or not */
256     bool tcp_nodelay;
257     // You can't change the purpose of a port dynamically (It is only
258     // used during startup
259     const bool management;
260 };
261 
262 /**
263  * Global stats.
264  */
265 struct stats {
266     /** Number of connections used by the server itself (listen ports etc). */
267     Couchbase::RelaxedAtomic<unsigned int> daemon_conns;
268 
269     /** The current number of connections to the server */
270     std::atomic<unsigned int> curr_conns;
271 
272     /** The total number of connections to the server since start (or reset) */
273     Couchbase::RelaxedAtomic<unsigned int> total_conns;
274 
275     /** The current number of allocated connection objects */
276     Couchbase::RelaxedAtomic<unsigned int> conn_structs;
277 
278     /** The number of times I reject a client */
279     Couchbase::RelaxedAtomic<uint64_t> rejected_conns;
280 
281     std::vector<ListeningPort> listening_ports;
282 };
283 
284 class Connection;
285 struct thread_stats* get_thread_stats(Connection* c);
286 
287 /*
288  *  Macros for managing statistics inside memcached
289  */
290 
291 /* The item must always be called "it" */
292 #define SLAB_GUTS(conn, thread_stats, slab_op, thread_op) \
293     thread_stats->slab_op++;
294 
295 #define THREAD_GUTS(conn, thread_stats, slab_op, thread_op) \
296     thread_stats->thread_op++;
297 
298 #define THREAD_GUTS2(conn, thread_stats, slab_op, thread_op) \
299     thread_stats->slab_op++; \
300     thread_stats->thread_op++;
301 
302 #define SLAB_THREAD_GUTS(conn, thread_stats, slab_op, thread_op) \
303     SLAB_GUTS(conn, thread_stats, slab_op, thread_op) \
304     THREAD_GUTS(conn, thread_stats, slab_op, thread_op)
305 
306 #define STATS_INCR1(GUTS, conn, slab_op, thread_op) { \
307     struct thread_stats *thread_stats = get_thread_stats(conn); \
308     GUTS(conn, thread_stats, slab_op, thread_op); \
309 }
310 
311 #define STATS_INCR(conn, op) \
312     STATS_INCR1(THREAD_GUTS, conn, op, op)
313 
314 #define SLAB_INCR(conn, op) \
315     STATS_INCR1(SLAB_GUTS, conn, op, op)
316 
317 #define STATS_TWO(conn, slab_op, thread_op) \
318     STATS_INCR1(THREAD_GUTS2, conn, slab_op, thread_op)
319 
320 #define SLAB_TWO(conn, slab_op, thread_op) \
321     STATS_INCR1(SLAB_THREAD_GUTS, conn, slab_op, thread_op)
322 
323 #define STATS_HIT(conn, op) \
324     SLAB_TWO(conn, op##_hits, cmd_##op)
325 
326 #define STATS_MISS(conn, op) \
327     STATS_TWO(conn, op##_misses, cmd_##op)
328 
329 /*
330  * Set the statistic to the maximum of the current value, and the specified
331  * value.
332  */
333 #define STATS_MAX(conn, op, value) get_thread_stats(conn)->op.setIfGreater(value);
334 
335 extern std::mutex stats_mutex;
336 extern char reset_stats_time[80];
337 
338 class Cookie;
339 void stats_reset(Cookie& cookie);
340