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"
31 static memcached_return_t
print_server_version(const memcached_st
*,
32 const memcached_instance_st
*instance
, void *) {
33 std::cerr
<< memcached_server_name(instance
) << ":" << memcached_server_port(instance
) << " "
34 << int(memcached_server_major_version(instance
)) << "."
35 << int(memcached_server_minor_version(instance
)) << "."
36 << int(memcached_server_micro_version(instance
)) << std::endl
;
38 return MEMCACHED_SUCCESS
;
41 static void print_report(memcached_st
*memc
, memcached_analysis_st
*report
) {
42 uint32_t server_count
= memcached_server_count(memc
);
43 auto most_consumed_server
= memcached_server_instance_by_position(memc
, report
->most_consumed_server
);
44 auto least_free_server
= memcached_server_instance_by_position(memc
, report
->least_free_server
);
45 auto oldest_server
= memcached_server_instance_by_position(memc
, report
->oldest_server
);
47 printf("Memcached Cluster Analysis Report\n\n");
48 printf("\tNumber of Servers Analyzed : %u\n", server_count
);
49 printf("\tAverage Item Size (incl/overhead) : %u bytes\n", report
->average_item_size
);
51 if (server_count
== 1) {
52 printf("\nFor a detailed report, you must supply multiple servers.\n");
57 printf("\tNode with most memory consumption : %s:%u (%llu bytes)\n",
58 memcached_server_name(most_consumed_server
),
59 (uint32_t) memcached_server_port(most_consumed_server
),
60 (unsigned long long) report
->most_used_bytes
);
61 printf("\tNode with least free space : %s:%u (%llu bytes remaining)\n",
62 memcached_server_name(least_free_server
),
63 (uint32_t) memcached_server_port(least_free_server
),
64 (unsigned long long) report
->least_remaining_bytes
);
65 printf("\tNode with longest uptime : %s:%u (%us)\n",
66 memcached_server_name(oldest_server
), (uint32_t) memcached_server_port(oldest_server
),
67 report
->longest_uptime
);
68 printf("\tPool-wide Hit Ratio : %1.f%%\n", report
->pool_hit_ratio
);
72 static bool analyze_stat(const client_options
&opt
, memcached_st
*memc
, memcached_stat_st
*stat
) {
73 memcached_return_t rc
;
74 auto report
= memcached_analyze(memc
, stat
, &rc
);
76 if (rc
!= MEMCACHED_SUCCESS
|| !report
) {
77 if (!opt
.isset("quiet")) {
78 std::cerr
<< "Failure to analyze servers:" << memcached_strerror(memc
, rc
) << ".\n";
82 print_report(memc
, report
);
87 static void latency_test(uint32_t iterations
, std::vector
<memcached_st
> &servers
) {
88 const char *test_key
= "libmemcached_test_key";
89 size_t test_key_len
= strlen(test_key
);
90 const memcached_instance_st
*slowest_server
= nullptr;
91 time_point::duration slowest_time
{};
92 std::vector
<const memcached_instance_st
*> failed_servers
{};
94 std::cout
<< "Network Latency Test:\n\n" << std::showpoint
<< std::fixed
<< std::setprecision(3);
96 for (auto &memc
: servers
) {
97 memcached_return_t rc
= memcached_last_error(&memc
);
99 auto start
= time_clock::now();
100 for (auto i
= 0u; i
< iterations
; ++i
) {
101 free(memcached_get(&memc
, test_key
, test_key_len
, nullptr, nullptr, &rc
));
102 if (memcached_fatal(rc
)) {
106 auto elapsed
= time_clock::now() - start
;
108 auto inst
= memcached_server_instance_by_position(&memc
, 0);
109 std::cout
<< "\t " << memcached_server_name(inst
)
110 << " (" << memcached_server_port(inst
) << ") ";
112 if (memcached_fatal(rc
)) {
113 std::cout
<< " => failed to reach the server\n";
114 failed_servers
.push_back(inst
);
117 << time_format(elapsed
/iterations
).count() << " seconds ("
118 << time_format_ms(elapsed
/iterations
).count() << "ms)\n";
119 if (slowest_time
== time_point::duration::zero() || slowest_time
< elapsed
) {
120 slowest_time
= elapsed
;
121 slowest_server
= inst
;
126 if (servers
.size() > 1 && slowest_server
) {
127 std::cout
<< "\n---\n\nSlowest Server: "
128 << memcached_server_name(slowest_server
) << "("
129 << memcached_server_port(slowest_server
) << ")"
131 << time_format(slowest_time
/iterations
).count() << " seconds ("
132 << time_format_ms(slowest_time
/iterations
).count() << "ms)\n";
134 if (!failed_servers
.empty()) {
135 for (const auto inst
: failed_servers
) {
136 std::cout
<< "Failed Server: " << memcached_server_name(inst
)
137 << " (" << memcached_server_port(inst
)
138 << ") => " << memcached_strerror(inst
->root
, memcached_server_error_return(inst
))
144 static bool analyze_latency(client_options
&opt
, memcached_st
*root
) {
145 uint32_t num_of_tests
= DEFAULT_LATENCY_ITERATIONS
;
147 if (auto iter_str
= opt
.argof("iterations")) {
148 num_of_tests
= std::stoul(iter_str
);
151 std::vector
<memcached_st
> servers
{memcached_server_count(root
)};
154 for (auto &memc
: servers
) {
155 memcached_clone(&memc
, root
);
156 memcached_servers_reset(&memc
);
157 auto instance
= memcached_server_instance_by_position(root
, i
++);
158 memcached_server_add(&memc
, memcached_server_name(instance
), memcached_server_port(instance
));
160 memcached_version(&memc
);
163 latency_test(num_of_tests
, servers
);
165 for (auto &memc
: servers
) {
166 memcached_free(&memc
);
172 static memcached_return_t
print_stat(const memcached_instance_st
*server
,
173 const char *key
, size_t key_length
,
174 const char *value
, size_t value_length
, void *context
) {
175 auto instance
= static_cast<const memcached_instance_st
**>(context
);
177 if (*instance
!= server
) {
180 std::cout
<< "Server: " << memcached_server_name(server
)
181 << " (" << memcached_server_port(server
) << ")\n";
185 std::cout
.write(key
, key_length
) << ": ";
186 std::cout
.write(value
, value_length
) << "\n";
188 return MEMCACHED_SUCCESS
;
191 static bool memstat(const client_options
&opt
, memcached_st
&memc
, const char *arg
) {
192 memcached_instance_st
*context
= nullptr;
193 auto rc
= memcached_stat_execute(&memc
, arg
, print_stat
, &context
);
194 if (memcached_success(rc
)) {
197 if (!opt
.isset("quiet")) {
198 std::cerr
<< "Failed to 'STAT " << (arg
? arg
: "") << "': ";
199 if (memcached_last_error(&memc
)) {
200 std::cerr
<< memcached_last_error_message(&memc
) << "\n";
202 std::cerr
<< memcached_strerror(&memc
, rc
) << "\n";
208 int main(int argc
, char *argv
[]) {
209 client_options opt
{PROGRAM_NAME
, PROGRAM_VERSION
, PROGRAM_DESCRIPTION
, "[stat ...]"};
211 for (const auto &def
: opt
.defaults
) {
212 if (def
.opt
.val
!= 'H') {
213 // no need for --hash
218 opt
.add("args", 'A', required_argument
, "Stat args. DEPRECATED: use positional arguments.");
219 opt
.add("server-version", 'S', no_argument
, "Print server version.");
220 opt
.add("analyze", 'a', optional_argument
, "Analyze server characteristics (options: default, latency).");
221 opt
.add("iterations", 0, required_argument
, "Iteration count of GETs sent by the latency test (default: 1000).");
223 char **argp
= nullptr;
224 if (!opt
.parse(argc
, argv
, &argp
)) {
229 if (!check_memcached(opt
, memc
)) {
233 if (!opt
.apply(&memc
)) {
234 memcached_free(&memc
);
238 auto exit_code
= EXIT_SUCCESS
;
239 if (opt
.isset('S')) {
240 if (opt
.isset("verbose")) {
241 std::cout
<< "Server versions:\n";
243 if (MEMCACHED_SUCCESS
!= memcached_version(&memc
)) {
244 exit_code
= EXIT_FAILURE
;
246 memcached_server_fn cb
[] = {&print_server_version
};
247 memcached_server_cursor(&memc
, cb
, nullptr, 1);
251 if (opt
.isset("analyze")) {
252 const char *analyze
= opt
.argof("analyze");
253 if (analyze
&& strcmp(analyze
, "default")) {
254 if (!strcmp(analyze
, "latency")) {
255 if (!analyze_latency(opt
, &memc
)) {
256 exit_code
= EXIT_FAILURE
;
261 if (!opt
.isset("quiet")) {
262 std::cerr
<< "Unknown --analyze mode: '" << analyze
<< "'.\n";
266 memcached_return_t rc
;
267 auto stat
= memcached_stat(&memc
, nullptr, &rc
);
268 if (!memcached_success(rc
)) {
269 exit_code
= EXIT_FAILURE
;
270 if (!opt
.isset("quiet")) {
271 std::cerr
<< memcached_last_error_message(&memc
) << "\n";
273 } else if (!analyze_stat(opt
, &memc
, stat
)) {
274 exit_code
= EXIT_FAILURE
;
276 memcached_stat_free(&memc
, stat
);
280 if (!*argp
|| opt
.isset('A')) {
281 if (!memstat(opt
, memc
, opt
.argof('A'))) {
282 exit_code
= EXIT_FAILURE
;
285 for (auto arg
= argp
; *arg
; ++arg
) {
286 if (!memstat(opt
, memc
, *arg
)) {
287 exit_code
= EXIT_FAILURE
;
292 memcached_free(&memc
);