Skip to content

Commit aa15a2d

Browse files
ZNeumannlavarou
andauthored
feat(agent): Memcached instance metrics with host name (#958)
These metrics can be used by the backend to create AWS relationship maps with memcached --------- Co-authored-by: Michal Nowacki <[email protected]>
1 parent 71674c9 commit aa15a2d

19 files changed

+404
-1
lines changed

agent/Makefile.frag

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ TEST_BINARIES = \
9393
tests/test_internal_instrument \
9494
tests/test_hash \
9595
tests/test_lib_aws_sdk_php \
96+
tests/test_memcached \
9697
tests/test_mongodb \
9798
tests/test_monolog \
9899
tests/test_mysql \

agent/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ if test "$PHP_NEWRELIC" = "yes"; then
215215
php_error.c php_execute.c php_explain.c php_explain_mysqli.c \
216216
php_explain_pdo_mysql.c php_extension.c php_file_get_contents.c \
217217
php_globals.c php_hash.c php_header.c php_httprequest_send.c \
218-
php_internal_instrument.c php_minit.c php_mshutdown.c php_mysql.c \
218+
php_internal_instrument.c php_memcached.c php_minit.c php_mshutdown.c php_mysql.c \
219219
php_mysqli.c php_newrelic.c php_nrini.c php_observer.c php_output.c php_pdo.c \
220220
php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c \
221221
php_rinit.c php_rshutdown.c php_samplers.c php_stack.c \

agent/php_internal_instrument.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#include "php_explain_mysqli.h"
1414
#include "php_file_get_contents.h"
1515
#include "php_globals.h"
16+
#include "php_hash.h"
1617
#include "php_httprequest_send.h"
1718
#include "php_internal_instrument.h"
19+
#include "php_memcached.h"
1820
#include "php_mysql.h"
1921
#include "php_mysqli.h"
2022
#include "php_pdo.h"
@@ -1531,6 +1533,57 @@ NR_INNER_WRAPPER(memcache_function) {
15311533
INTERNAL_FUNCTION_PARAM_PASSTHRU);
15321534
}
15331535

1536+
NR_INNER_WRAPPER(memcached_add_server) {
1537+
char* host = NULL;
1538+
nr_string_len_t host_len = 0;
1539+
zend_long port = 0;
1540+
zend_long weight = 0;
1541+
int zcaught = 0;
1542+
1543+
if (SUCCESS
1544+
== zend_parse_parameters_ex(
1545+
ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|ll", &host,
1546+
&host_len, &port, &weight) &&
1547+
NULL != host) {
1548+
nr_php_memcached_create_instance_metric(host, port);
1549+
}
1550+
zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler,
1551+
INTERNAL_FUNCTION_PARAM_PASSTHRU);
1552+
if (zcaught) {
1553+
zend_bailout();
1554+
/* NOTREACHED */
1555+
}
1556+
}
1557+
1558+
NR_INNER_WRAPPER(memcached_add_servers) {
1559+
zval* servers = NULL;
1560+
zval* server = NULL;
1561+
int zcaught = 0;
1562+
1563+
if (SUCCESS
1564+
== zend_parse_parameters_ex(
1565+
ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "a", &servers)) {
1566+
if (NULL != servers && Z_TYPE_P(servers) == IS_ARRAY) {
1567+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(servers), server) {
1568+
zval* host = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 0);
1569+
zval* port = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 1);
1570+
if (nr_php_is_zval_valid_string(host) &&
1571+
nr_php_is_zval_valid_integer(port)) {
1572+
nr_php_memcached_create_instance_metric(Z_STRVAL_P(host), Z_LVAL_P(port));
1573+
}
1574+
}
1575+
ZEND_HASH_FOREACH_END();
1576+
}
1577+
}
1578+
zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler,
1579+
INTERNAL_FUNCTION_PARAM_PASSTHRU);
1580+
1581+
if (zcaught) {
1582+
zend_bailout();
1583+
/* NOTREACHED */
1584+
}
1585+
}
1586+
15341587
/*
15351588
* Handle
15361589
* bool redis::connect ( string $host[, int $port = 6379 ... ] )
@@ -3098,6 +3151,8 @@ NR_OUTER_WRAPPER(memcached_set)
30983151
NR_OUTER_WRAPPER(memcached_setbykey)
30993152
NR_OUTER_WRAPPER(memcached_setmulti)
31003153
NR_OUTER_WRAPPER(memcached_setmultibykey)
3154+
NR_OUTER_WRAPPER(memcached_addserver)
3155+
NR_OUTER_WRAPPER(memcached_addservers)
31013156

31023157
NR_OUTER_WRAPPER(redis_append)
31033158
NR_OUTER_WRAPPER(redis_bitcount)
@@ -3511,6 +3566,10 @@ void nr_php_generate_internal_wrap_records(void) {
35113566
memcache_function, 0, "set")
35123567
NR_INTERNAL_WRAPREC("memcached::setmultibykey", memcached_setmultibykey,
35133568
memcache_function, 0, "set")
3569+
NR_INTERNAL_WRAPREC("memcached::addserver", memcached_addserver,
3570+
memcached_add_server, 0, 0);
3571+
NR_INTERNAL_WRAPREC("memcached::addservers", memcached_addservers,
3572+
memcached_add_servers, 0, 0);
35143573

35153574
NR_INTERNAL_WRAPREC("redis::connect", redis_connect, redis_connect, 0,
35163575
"connect")

agent/php_memcached.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "php_memcached.h"
7+
#include "nr_datastore_instance.h"
8+
#include "php_agent.h"
9+
10+
nr_datastore_instance_t* nr_php_memcached_create_datastore_instance(
11+
const char* host_or_socket,
12+
zend_long port) {
13+
nr_datastore_instance_t* instance = NULL;
14+
if (port == 0) { // local socket
15+
instance = nr_datastore_instance_create("localhost", host_or_socket, NULL);
16+
} else {
17+
char* port_str = nr_formatf("%ld", (long)port);
18+
instance = nr_datastore_instance_create(host_or_socket, port_str, NULL);
19+
nr_free(port_str);
20+
}
21+
return instance;
22+
}
23+
24+
void nr_php_memcached_create_instance_metric(
25+
const char* host_or_socket,
26+
zend_long port) {
27+
nr_datastore_instance_t* instance
28+
= nr_php_memcached_create_datastore_instance(host_or_socket, port);
29+
char* instance_metric = nr_formatf("Datastore/instance/Memcached/%s/%s",
30+
instance->host, instance->port_path_or_id);
31+
nrm_force_add(NRPRG(txn)->unscoped_metrics, instance_metric, 0);
32+
nr_datastore_instance_destroy(&instance);
33+
nr_free(instance_metric);
34+
}

agent/php_memcached.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef PHP_MEMCACHED_HDR
7+
#define PHP_MEMCACHED_HDR
8+
9+
#include "nr_datastore_instance.h"
10+
#include "php_includes.h"
11+
12+
/*
13+
* Purpose : Create a datastore instance metadata for a Memcached server.
14+
*
15+
* Params : 1. The memcached host or socket name as given to Memcached::addServer().
16+
* 2. The memcached port as given as given to Memcached::addServer().
17+
*
18+
* Returns: nr_datastore_instance_t* that the caller is responsible for freeing
19+
*/
20+
nr_datastore_instance_t* nr_php_memcached_create_datastore_instance(
21+
const char* host_or_socket,
22+
zend_long port);
23+
24+
/*
25+
* Purpose : Create a memcached instance metric
26+
*
27+
* Params : 1. The memcached host or socket name as given to Memcached::addServer().
28+
* 2. The memcached port as given as given to Memcached::addServer().
29+
*/
30+
extern void nr_php_memcached_create_instance_metric(
31+
const char* host_or_socket,
32+
zend_long port);
33+
34+
35+
#endif

agent/tests/test_memcached.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#include "tlib_php.h"
6+
#include "tlib_datastore.h"
7+
8+
#include "php_agent.h"
9+
#include "php_memcached.h"
10+
#include "util_system.h"
11+
12+
tlib_parallel_info_t parallel_info
13+
= {.suggested_nthreads = -1, .state_size = 0};
14+
15+
static char* system_host_name;
16+
17+
static void test_create_datastore_instance(void) {
18+
assert_datastore_instance_equals_destroy(
19+
"named socket",
20+
&((nr_datastore_instance_t){
21+
.host = system_host_name,
22+
.database_name = "unknown",
23+
.port_path_or_id = "/tmp/memcached.sock",
24+
}),
25+
nr_php_memcached_create_datastore_instance("/tmp/memcached.sock", 0));
26+
27+
assert_datastore_instance_equals_destroy(
28+
"empty socket",
29+
&((nr_datastore_instance_t){
30+
.host = system_host_name,
31+
.database_name = "unknown",
32+
.port_path_or_id = "unknown",
33+
}),
34+
nr_php_memcached_create_datastore_instance("", 0));
35+
36+
assert_datastore_instance_equals_destroy(
37+
"empty host",
38+
&((nr_datastore_instance_t){
39+
.host = system_host_name,
40+
.database_name = "unknown",
41+
.port_path_or_id = "unknown",
42+
}),
43+
nr_php_memcached_create_datastore_instance(NULL, 0));
44+
45+
assert_datastore_instance_equals_destroy(
46+
"host.name socket",
47+
&((nr_datastore_instance_t){
48+
.host = "host.name",
49+
.database_name = "unknown",
50+
.port_path_or_id = "11211",
51+
}),
52+
nr_php_memcached_create_datastore_instance("host.name", 11211));
53+
54+
assert_datastore_instance_equals_destroy(
55+
"host and port",
56+
&((nr_datastore_instance_t){
57+
.host = "unknown",
58+
.database_name = "unknown",
59+
.port_path_or_id = "6379",
60+
}),
61+
nr_php_memcached_create_datastore_instance("", 6379));
62+
63+
assert_datastore_instance_equals_destroy(
64+
"NULL socket",
65+
&((nr_datastore_instance_t){
66+
.host = "unknown",
67+
.database_name = "unknown",
68+
.port_path_or_id = "11211",
69+
}),
70+
nr_php_memcached_create_datastore_instance(NULL, 11211));
71+
}
72+
73+
static void test_create_instance_metric(void) {
74+
nrtxn_t* txn;
75+
nrmetric_t* metric;
76+
char* metric_str;
77+
tlib_php_engine_create("");
78+
tlib_php_request_start();
79+
txn = NRPRG(txn);
80+
81+
nr_php_memcached_create_instance_metric("host", 11211);
82+
metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/host/11211");
83+
tlib_pass_if_not_null("metric found", metric);
84+
85+
nr_php_memcached_create_instance_metric("", 11211);
86+
metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/unknown/11211");
87+
tlib_pass_if_not_null("metric found", metric);
88+
89+
nr_php_memcached_create_instance_metric(NULL, 7);
90+
metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/unknown/7");
91+
tlib_pass_if_not_null("metric found", metric);
92+
93+
nr_php_memcached_create_instance_metric("path/to/sock", 0);
94+
metric_str = nr_formatf("Datastore/instance/Memcached/%s/path/to/sock", system_host_name);
95+
metric = nrm_find(txn->unscoped_metrics, metric_str);
96+
nr_free(metric_str);
97+
tlib_pass_if_not_null("metric found", metric);
98+
99+
nr_php_memcached_create_instance_metric("", 0);
100+
metric_str = nr_formatf("Datastore/instance/Memcached/%s/unknown", system_host_name);
101+
metric = nrm_find(txn->unscoped_metrics, metric_str);
102+
nr_free(metric_str);
103+
tlib_pass_if_not_null("metric found", metric);
104+
105+
// restart the transaction because the next metric is the same as a previous metric
106+
tlib_php_request_end();
107+
tlib_php_request_start();
108+
txn = NRPRG(txn);
109+
110+
nr_php_memcached_create_instance_metric(NULL, 0);
111+
metric_str = nr_formatf("Datastore/instance/Memcached/%s/unknown", system_host_name);
112+
metric = nrm_find(txn->unscoped_metrics, metric_str);
113+
nr_free(metric_str);
114+
tlib_pass_if_not_null("metric found", metric);
115+
116+
tlib_php_request_end();
117+
tlib_php_engine_destroy();
118+
}
119+
120+
void test_main(void* p NRUNUSED) {
121+
system_host_name = nr_system_get_hostname();
122+
123+
test_create_datastore_instance();
124+
test_create_instance_metric();
125+
126+
nr_free(system_host_name);
127+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
/*
3+
* Copyright 2020 New Relic Corporation. All rights reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*DESCRIPTION
8+
The agent should report instance metrics when multiple servers are
9+
added at once via Memcached::addServers()
10+
*/
11+
12+
/*SKIPIF
13+
<?php require('skipif.inc'); ?>
14+
*/
15+
16+
/*INI
17+
*/
18+
19+
/*EXPECT_METRICS_EXIST
20+
Datastore/instance/Memcached/host1/1, 1
21+
Datastore/instance/Memcached/host2/2, 1
22+
Datastore/instance/Memcached/host3/11211, 1
23+
Datastore/instance/Memcached/host4/1, 1
24+
*/
25+
26+
/*EXPECT_ERROR_EVENTS null */
27+
28+
require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php');
29+
require_once(realpath (dirname ( __FILE__ )) . '/../../include/tap.php');
30+
require_once(realpath (dirname ( __FILE__ )) . '/memcache.inc');
31+
32+
$memcached = new Memcached();
33+
$memcached->addServers(array(
34+
array("host1", 1),
35+
array("host2", 2),
36+
array("host3", 11211)));
37+
$memcached->addServers(array());
38+
$memcached->addServers(array(array("host4", 1, "test field")));
39+
$memcached->quit();

0 commit comments

Comments
 (0)