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 #include "common/options.hpp"
23 #include "common/checks.hpp"
24 #include "common/utilities.h"
30 static memcached_return_t
print_server_version(const memcached_st
*,
31 const memcached_instance_st
*instance
, void *) {
32 std::cerr
<< memcached_server_name(instance
) << ":" << memcached_server_port(instance
) << " "
33 << int(memcached_server_major_version(instance
)) << "."
34 << int(memcached_server_minor_version(instance
)) << "."
35 << int(memcached_server_micro_version(instance
)) << std::endl
;
37 return MEMCACHED_SUCCESS
;
40 static void print_report(memcached_st
*memc
, memcached_analysis_st
*report
) {
41 uint32_t server_count
= memcached_server_count(memc
);
42 auto most_consumed_server
= memcached_server_instance_by_position(memc
, report
->most_consumed_server
);
43 auto least_free_server
= memcached_server_instance_by_position(memc
, report
->least_free_server
);
44 auto oldest_server
= memcached_server_instance_by_position(memc
, report
->oldest_server
);
46 printf("Memcached Cluster Analysis Report\n\n");
47 printf("\tNumber of Servers Analyzed : %u\n", server_count
);
48 printf("\tAverage Item Size (incl/overhead) : %u bytes\n", report
->average_item_size
);
50 if (server_count
== 1) {
51 printf("\nFor a detailed report, you must supply multiple servers.\n");
56 printf("\tNode with most memory consumption : %s:%u (%llu bytes)\n",
57 memcached_server_name(most_consumed_server
),
58 (uint32_t) memcached_server_port(most_consumed_server
),
59 (unsigned long long) report
->most_used_bytes
);
60 printf("\tNode with least free space : %s:%u (%llu bytes remaining)\n",
61 memcached_server_name(least_free_server
),
62 (uint32_t) memcached_server_port(least_free_server
),
63 (unsigned long long) report
->least_remaining_bytes
);
64 printf("\tNode with longest uptime : %s:%u (%us)\n",
65 memcached_server_name(oldest_server
), (uint32_t) memcached_server_port(oldest_server
),
66 report
->longest_uptime
);
67 printf("\tPool-wide Hit Ratio : %1.f%%\n", report
->pool_hit_ratio
);
71 static bool analyze_stat(const client_options
&opt
, memcached_st
*memc
, memcached_stat_st
*stat
) {
72 memcached_return_t rc
;
73 auto report
= memcached_analyze(memc
, stat
, &rc
);
75 if (rc
!= MEMCACHED_SUCCESS
|| !report
) {
76 if (!opt
.isset("quiet")) {
77 std::cerr
<< "Failure to analyze servers:" << memcached_strerror(memc
, rc
) << ".\n";
81 print_report(memc
, report
);
86 using time_clock
= std::chrono::high_resolution_clock
;
87 using time_point
= std::chrono::time_point
<time_clock
>;
88 using time_format
= std::chrono::duration
<double, std::ratio
<1,1>>;
89 using time_format_ms
= std::chrono::duration
<double, std::ratio
<1,1000>>;
91 static void latency_test(uint32_t iterations
, std::vector
<memcached_st
> servers
) {
92 const char *test_key
= "libmemcached_test_key";
93 size_t test_key_len
= strlen(test_key
);
94 const memcached_instance_st
*slowest_server
= nullptr;
95 time_point::duration slowest_time
{};
97 std::cout
<< "Network Latency Test:\n\n" << std::showpoint
<< std::fixed
<< std::setprecision(3);
99 for (auto &memc
: servers
) {
100 memcached_return_t rc
;
102 auto start
= time_clock::now();
103 for (auto i
= 0u; i
< iterations
; ++i
) {
104 free(memcached_get(&memc
, test_key
, test_key_len
, nullptr, nullptr, &rc
));
105 if (memcached_fatal(rc
)) {
109 auto elapsed
= time_clock::now() - start
;
111 auto inst
= memcached_server_instance_by_position(&memc
, 0);
112 std::cout
<< "\t " << memcached_server_name(inst
)
113 << " (" << memcached_server_port(inst
) << ") ";
115 if (memcached_fatal(rc
)) {
116 std::cout
<< " => failed to reach the server\n";
119 << time_format(elapsed
/iterations
).count() << " seconds ("
120 << time_format_ms(elapsed
/iterations
).count() << "ms)\n";
121 if (slowest_time
== time_point::duration::zero() || slowest_time
< elapsed
) {
122 slowest_time
= elapsed
;
123 slowest_server
= inst
;
128 if (servers
.size() > 1 && slowest_server
) {
129 std::cout
<< "\n---\n\nSlowest Server: "
130 << memcached_server_name(slowest_server
) << "("
131 << memcached_server_port(slowest_server
) << ")"
133 << time_format(slowest_time
/iterations
).count() << " seconds ("
134 << time_format_ms(slowest_time
/iterations
).count() << "ms)\n";
138 static bool analyze_latency(client_options
&opt
, memcached_st
*root
) {
139 uint32_t num_of_tests
= 32;
141 std::vector
<memcached_st
> servers
{memcached_server_count(root
)};
144 for (auto &memc
: servers
) {
145 if (!check_memcached(opt
, memc
)) {
148 auto instance
= memcached_server_instance_by_position(root
, i
++);
149 memcached_server_add(&memc
, memcached_server_name(instance
), memcached_server_port(instance
));
152 latency_test(num_of_tests
, servers
);
154 for (auto &memc
: servers
) {
155 memcached_free(&memc
);
161 static memcached_return_t
print_stat(const memcached_instance_st
*server
,
162 const char *key
, size_t key_length
,
163 const char *value
, size_t value_length
, void *context
) {
164 auto instance
= static_cast<const memcached_instance_st
**>(context
);
166 if (*instance
!= server
) {
169 std::cout
<< "Server: " << memcached_server_name(server
)
170 << " (" << memcached_server_port(server
) << ")\n";
174 std::cout
.write(key
, key_length
) << ": ";
175 std::cout
.write(value
, value_length
) << "\n";
177 return MEMCACHED_SUCCESS
;
180 static bool memstat(const client_options
&opt
, memcached_st
&memc
, const char *arg
) {
181 memcached_instance_st
*context
= nullptr;
182 auto rc
= memcached_stat_execute(&memc
, arg
, print_stat
, &context
);
183 if (memcached_success(rc
)) {
186 if (!opt
.isset("quiet")) {
187 std::cerr
<< "Failed to 'STAT " << (arg
? arg
: "") << "': ";
188 if (memcached_last_error(&memc
)) {
189 std::cerr
<< memcached_last_error_message(&memc
) << "\n";
191 std::cerr
<< memcached_strerror(&memc
, rc
) << "\n";
197 int main(int argc
, char *argv
[]) {
198 client_options opt
{PROGRAM_NAME
, PROGRAM_VERSION
, PROGRAM_DESCRIPTION
, "[stat ...]"};
200 for (const auto &def
: opt
.defaults
) {
201 if (def
.opt
.val
!= 'H') {
202 // no need for --hash
207 opt
.add("args", 'A', required_argument
, "Stat args. DEPRECATED: use positional arguments.");
208 opt
.add("server-version", 'S', no_argument
, "Print server version.");
209 opt
.add("analyze", 'a', optional_argument
, "Analyze server characteristics (options: default, latency).");
211 char **argp
= nullptr;
212 if (!opt
.parse(argc
, argv
, &argp
)) {
217 if (!check_memcached(opt
, memc
)) {
221 if (!opt
.apply(&memc
)) {
222 memcached_free(&memc
);
226 auto exit_code
= EXIT_SUCCESS
;
227 if (opt
.isset('S')) {
228 if (opt
.isset("verbose")) {
229 std::cout
<< "Server versions:\n";
231 if (MEMCACHED_SUCCESS
!= memcached_version(&memc
)) {
232 exit_code
= EXIT_FAILURE
;
234 memcached_server_fn cb
[] = {&print_server_version
};
235 memcached_server_cursor(&memc
, cb
, nullptr, 1);
239 if (opt
.isset("analyze")) {
240 const char *analyze
= opt
.argof("analyze");
241 if (analyze
&& strcmp(analyze
, "default")) {
242 if (!strcmp(analyze
, "latency")) {
243 if (!analyze_latency(opt
, &memc
)) {
244 exit_code
= EXIT_FAILURE
;
249 if (!opt
.isset("quiet")) {
250 std::cerr
<< "Unknown --analyze mode: '" << analyze
<< "'.\n";
254 memcached_return_t rc
;
255 auto stat
= memcached_stat(&memc
, nullptr, &rc
);
256 if (!memcached_success(rc
)) {
257 exit_code
= EXIT_FAILURE
;
258 if (!opt
.isset("quiet")) {
259 std::cerr
<< memcached_last_error_message(&memc
) << "\n";
261 } else if (!analyze_stat(opt
, &memc
, stat
)) {
262 exit_code
= EXIT_FAILURE
;
264 memcached_stat_free(&memc
, stat
);
268 if (!*argp
|| opt
.isset('A')) {
269 if (!memstat(opt
, memc
, opt
.argof('A'))) {
270 exit_code
= EXIT_FAILURE
;
273 for (auto arg
= argp
; *arg
; ++arg
) {
274 if (!memstat(opt
, memc
, *arg
)) {
275 exit_code
= EXIT_FAILURE
;
280 memcached_free(&memc
);