2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
16 #include "mem_config.h"
18 #define PROGRAM_NAME "memstat"
19 #define PROGRAM_DESCRIPTION "Print stats/version of or analyze a memcached cluster."
20 #define PROGRAM_VERSION "1.1"
22 #define DEFAULT_LATENCY_ITERATIONS 100 // update help string, if changed
24 #include "common/options.hpp"
25 #include "common/checks.hpp"
26 #include "common/time.hpp"
27 #include "common/utilities.h"
32 static memcached_return_t
print_server_version(const memcached_st
*,
33 const memcached_instance_st
*instance
, void *) {
34 std::cerr
<< memcached_server_name(instance
) << ":" << memcached_server_port(instance
) << " "
35 << int(memcached_server_major_version(instance
)) << "."
36 << int(memcached_server_minor_version(instance
)) << "."
37 << int(memcached_server_micro_version(instance
)) << std::endl
;
39 return MEMCACHED_SUCCESS
;
42 static void print_report(memcached_st
*memc
, memcached_analysis_st
*report
) {
43 uint32_t server_count
= memcached_server_count(memc
);
44 auto most_consumed_server
= memcached_server_instance_by_position(memc
, report
->most_consumed_server
);
45 auto least_free_server
= memcached_server_instance_by_position(memc
, report
->least_free_server
);
46 auto oldest_server
= memcached_server_instance_by_position(memc
, report
->oldest_server
);
48 printf("Memcached Cluster Analysis Report\n\n");
49 printf("\tNumber of Servers Analyzed : %u\n", server_count
);
50 printf("\tAverage Item Size (incl/overhead) : %u bytes\n", report
->average_item_size
);
52 if (server_count
== 1) {
53 printf("\nFor a detailed report, you must supply multiple servers.\n");
58 printf("\tNode with most memory consumption : %s:%u (%llu bytes)\n",
59 memcached_server_name(most_consumed_server
),
60 (uint32_t) memcached_server_port(most_consumed_server
),
61 (unsigned long long) report
->most_used_bytes
);
62 printf("\tNode with least free space : %s:%u (%llu bytes remaining)\n",
63 memcached_server_name(least_free_server
),
64 (uint32_t) memcached_server_port(least_free_server
),
65 (unsigned long long) report
->least_remaining_bytes
);
66 printf("\tNode with longest uptime : %s:%u (%us)\n",
67 memcached_server_name(oldest_server
), (uint32_t) memcached_server_port(oldest_server
),
68 report
->longest_uptime
);
69 printf("\tPool-wide Hit Ratio : %1.f%%\n", report
->pool_hit_ratio
);
73 static bool analyze_stat(const client_options
&opt
, memcached_st
*memc
, memcached_stat_st
*stat
) {
74 memcached_return_t rc
;
75 auto report
= memcached_analyze(memc
, stat
, &rc
);
77 if (rc
!= MEMCACHED_SUCCESS
|| !report
) {
78 if (!opt
.isset("quiet")) {
79 std::cerr
<< "Failure to analyze servers:" << memcached_strerror(memc
, rc
) << ".\n";
83 print_report(memc
, report
);
88 static void latency_test(uint32_t iterations
, std::vector
<memcached_st
> &servers
) {
89 const char *test_key
= "libmemcached_test_key";
90 size_t test_key_len
= strlen(test_key
);
91 const memcached_instance_st
*slowest_server
= nullptr;
92 time_point::duration slowest_time
{};
93 std::vector
<const memcached_instance_st
*> failed_servers
{};
95 std::cout
<< "Network Latency Test:\n\n" << std::showpoint
<< std::fixed
<< std::setprecision(3);
97 for (auto &memc
: servers
) {
98 memcached_return_t rc
= memcached_last_error(&memc
);
100 auto start
= time_clock::now();
101 for (auto i
= 0u; i
< iterations
; ++i
) {
102 free(memcached_get(&memc
, test_key
, test_key_len
, nullptr, nullptr, &rc
));
103 if (memcached_fatal(rc
)) {
107 auto elapsed
= time_clock::now() - start
;
109 auto inst
= memcached_server_instance_by_position(&memc
, 0);
110 std::cout
<< "\t " << memcached_server_name(inst
)
111 << " (" << memcached_server_port(inst
) << ") ";
113 if (memcached_fatal(rc
)) {
114 std::cout
<< " => failed to reach the server\n";
115 failed_servers
.push_back(inst
);
118 << time_format(elapsed
/iterations
).count() << " seconds ("
119 << time_format_ms(elapsed
/iterations
).count() << "ms)\n";
120 if (slowest_time
== time_point::duration::zero() || slowest_time
< elapsed
) {
121 slowest_time
= elapsed
;
122 slowest_server
= inst
;
127 if (servers
.size() > 1 && slowest_server
) {
128 std::cout
<< "\n---\n\nSlowest Server: "
129 << memcached_server_name(slowest_server
) << "("
130 << memcached_server_port(slowest_server
) << ")"
132 << time_format(slowest_time
/iterations
).count() << " seconds ("
133 << time_format_ms(slowest_time
/iterations
).count() << "ms)\n";
135 if (!failed_servers
.empty()) {
136 for (const auto inst
: failed_servers
) {
137 std::cout
<< "Failed Server: " << memcached_server_name(inst
)
138 << " (" << memcached_server_port(inst
)
139 << ") => " << memcached_strerror(inst
->root
, memcached_server_error_return(inst
))
145 static bool analyze_latency(client_options
&opt
, memcached_st
*root
) {
146 uint32_t num_of_tests
= DEFAULT_LATENCY_ITERATIONS
;
148 if (auto iter_str
= opt
.argof("iterations")) {
149 num_of_tests
= std::stoul(iter_str
);
152 std::vector
<memcached_st
> servers
{memcached_server_count(root
)};
155 for (auto &memc
: servers
) {
156 memcached_clone(&memc
, root
);
157 memcached_servers_reset(&memc
);
158 auto instance
= memcached_server_instance_by_position(root
, i
++);
159 memcached_server_add(&memc
, memcached_server_name(instance
), memcached_server_port(instance
));
161 memcached_version(&memc
);
164 latency_test(num_of_tests
, servers
);
166 for (auto &memc
: servers
) {
167 memcached_free(&memc
);
173 static memcached_return_t
print_stat(const memcached_instance_st
*server
,
174 const char *key
, size_t key_length
,
175 const char *value
, size_t value_length
, void *context
) {
176 auto instance
= static_cast<const memcached_instance_st
**>(context
);
178 if (*instance
!= server
) {
181 std::cout
<< "Server: " << memcached_server_name(server
)
182 << " (" << memcached_server_port(server
) << ")\n";
186 std::cout
.write(key
, key_length
) << ": ";
187 std::cout
.write(value
, value_length
) << "\n";
189 return MEMCACHED_SUCCESS
;
192 static bool memstat(const client_options
&opt
, memcached_st
&memc
, const char *arg
) {
193 memcached_instance_st
*context
= nullptr;
194 auto rc
= memcached_stat_execute(&memc
, arg
, print_stat
, &context
);
195 if (memcached_success(rc
)) {
198 if (!opt
.isset("quiet")) {
199 std::cerr
<< "Failed to 'STAT " << (arg
? arg
: "") << "': ";
200 if (memcached_last_error(&memc
)) {
201 std::cerr
<< memcached_last_error_message(&memc
) << "\n";
203 std::cerr
<< memcached_strerror(&memc
, rc
) << "\n";
209 int main(int argc
, char *argv
[]) {
210 client_options opt
{PROGRAM_NAME
, PROGRAM_VERSION
, PROGRAM_DESCRIPTION
, "[stat ...]"};
212 for (const auto &def
: opt
.defaults
) {
213 if (def
.opt
.val
!= 'H') {
214 // no need for --hash
219 opt
.add("args", 'A', required_argument
, "Stat args. DEPRECATED: use positional arguments.");
220 opt
.add("server-version", 'S', no_argument
, "Print server version.");
221 opt
.add("analyze", 'a', optional_argument
, "Analyze server characteristics (options: default, latency).");
222 opt
.add("iterations", 0, required_argument
, "Iteration count of GETs sent by the latency test (default: 1000).");
224 char **argp
= nullptr;
225 if (!opt
.parse(argc
, argv
, &argp
)) {
230 if (!check_memcached(opt
, memc
)) {
234 if (!opt
.apply(&memc
)) {
235 memcached_free(&memc
);
239 auto exit_code
= EXIT_SUCCESS
;
240 if (opt
.isset('S')) {
241 if (opt
.isset("verbose")) {
242 std::cout
<< "Server versions:\n";
244 if (MEMCACHED_SUCCESS
!= memcached_version(&memc
)) {
245 exit_code
= EXIT_FAILURE
;
247 memcached_server_fn cb
[] = {&print_server_version
};
248 memcached_server_cursor(&memc
, cb
, nullptr, 1);
252 if (opt
.isset("analyze")) {
253 const char *analyze
= opt
.argof("analyze");
254 if (analyze
&& strcmp(analyze
, "default")) {
255 if (!strcmp(analyze
, "latency")) {
256 if (!analyze_latency(opt
, &memc
)) {
257 exit_code
= EXIT_FAILURE
;
262 if (!opt
.isset("quiet")) {
263 std::cerr
<< "Unknown --analyze mode: '" << analyze
<< "'.\n";
267 memcached_return_t rc
;
268 auto stat
= memcached_stat(&memc
, nullptr, &rc
);
269 if (!memcached_success(rc
)) {
270 exit_code
= EXIT_FAILURE
;
271 if (!opt
.isset("quiet")) {
272 std::cerr
<< memcached_last_error_message(&memc
) << "\n";
274 } else if (!analyze_stat(opt
, &memc
, stat
)) {
275 exit_code
= EXIT_FAILURE
;
277 memcached_stat_free(&memc
, stat
);
281 if (!*argp
|| opt
.isset('A')) {
282 if (!memstat(opt
, memc
, opt
.argof('A'))) {
283 exit_code
= EXIT_FAILURE
;
286 for (auto arg
= argp
; *arg
; ++arg
) {
287 if (!memstat(opt
, memc
, *arg
)) {
288 exit_code
= EXIT_FAILURE
;
293 memcached_free(&memc
);