summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHemal Shah <hemal@stripe.com>2022-10-19 19:17:13 -0700
committerdormando <dormando@rydia.net>2023-03-15 17:16:36 -0700
commit9b3c946f485309cdf50f7fb1622707b5373e33f3 (patch)
tree50442521e1d2912caddfe0266f5223b0f4b48fb3
parentc04701654413719d4abd7645c6d7b3fba4255e85 (diff)
downloadmemcached-9b3c946f485309cdf50f7fb1622707b5373e33f3.tar.gz
log: Add a new watcher to watch for deletions.
`watch deletions`: would log all keys which are deleted using either `delete` or `md` command. The log line would contain the command used, the key, the clsid and size of the deleted item. Items which result in delete miss or are marked as stale wouldn't show up in the logs
-rw-r--r--doc/protocol.txt5
-rw-r--r--logger.c33
-rw-r--r--logger.h10
-rw-r--r--memcached.h3
-rw-r--r--proto_text.c5
-rwxr-xr-xt/watcher.t49
6 files changed, 102 insertions, 3 deletions
diff --git a/doc/protocol.txt b/doc/protocol.txt
index 8a261ed..cd476db 100644
--- a/doc/protocol.txt
+++ b/doc/protocol.txt
@@ -1174,7 +1174,7 @@ Watchers are a way to connect to memcached and inspect what's going on
internally. This is an evolving feature so new endpoints should show up over
time.
-watch <fetchers|mutations|evictions|connevents>
+watch <fetchers|mutations|evictions|connevents|deletions>
- Turn connection into a watcher. Options can be stacked and are
space-separated. Logs will be sent to the watcher until it disconnects.
@@ -1223,6 +1223,9 @@ The arguments are:
- "proxyuser": Emits log entries created from lua configuration files.
+- "deletions": Emits logs when an item is successfully deleted from the
+ cache using `delete` or `md` commands. delete misses wouldn't be logged
+
Statistics
----------
diff --git a/logger.c b/logger.c
index 3f483ed..1ff7211 100644
--- a/logger.c
+++ b/logger.c
@@ -137,6 +137,18 @@ static void _logger_log_item_store(logentry *e, const entry_details *d, const vo
e->size = sizeof(struct logentry_item_store) + nkey;
}
+static void _logger_log_item_deleted(logentry *e, const entry_details *d, const void *entry, va_list ap) {
+ item *it = (item *)entry;
+ int comm = va_arg(ap, int);
+ struct logentry_deletion *le = (struct logentry_deletion *) e->data;
+ le->nkey = it->nkey;
+ le->cmd = comm;
+ le->nbytes = it->nbytes;
+ le->clsid = ITEM_clsid(it);
+ memcpy(le->key, ITEM_key(it), it->nkey);
+ e->size = sizeof(struct logentry_deletion) + le->nkey;
+}
+
static void _logger_log_conn_event(logentry *e, const entry_details *d, const void *entry, va_list ap) {
struct sockaddr_in6 *addr = va_arg(ap, struct sockaddr_in6 *);
socklen_t addrlen = va_arg(ap, socklen_t);
@@ -251,6 +263,26 @@ static int _logger_parse_ee(logentry *e, char *scratch) {
return total;
}
+static int _logger_parse_ide(logentry *e, char *scratch) {
+ int total;
+ const char *cmd = "na";
+ const char * const cmd_map[] = {
+ "null", "delete", "md" };
+ char keybuf[KEY_MAX_URI_ENCODED_LENGTH];
+ struct logentry_deletion *le = (struct logentry_deletion *) e->data;
+ uriencode(le->key, keybuf, le->nkey, KEY_MAX_URI_ENCODED_LENGTH);
+
+ if (le->cmd <= 2)
+ cmd = cmd_map[le->cmd];
+
+ total = snprintf(scratch, LOGGER_PARSE_SCRATCH,
+ "ts=%d.%d gid=%llu type=deleted key=%s cmd=%s clsid=%u size=%d\n",
+ (int)e->tv.tv_sec, (int)e->tv.tv_usec, (unsigned long long) e->gid,
+ keybuf, cmd, le->clsid,
+ le->nbytes > 0 ? le->nbytes - 2 : 0); // CLRF
+ return total;
+}
+
#ifdef EXTSTORE
static int _logger_parse_extw(logentry *e, char *scratch) {
int total;
@@ -445,6 +477,7 @@ static const entry_details default_entries[] = {
},
[LOGGER_CONNECTION_NEW] = {512, LOG_CONNEVENTS, _logger_log_conn_event, _logger_parse_cne, NULL},
[LOGGER_CONNECTION_CLOSE] = {512, LOG_CONNEVENTS, _logger_log_conn_event, _logger_parse_cce, NULL},
+ [LOGGER_DELETIONS] = {512, LOG_DELETIONS, _logger_log_item_deleted, _logger_parse_ide, NULL},
#ifdef EXTSTORE
[LOGGER_EXTSTORE_WRITE] = {512, LOG_EVICTIONS, _logger_log_ext_write, _logger_parse_extw, NULL},
[LOGGER_COMPACT_START] = {512, LOG_SYSEVENTS, _logger_log_text, _logger_parse_text,
diff --git a/logger.h b/logger.h
index 4e8f1bb..9d2630c 100644
--- a/logger.h
+++ b/logger.h
@@ -22,6 +22,7 @@ enum log_entry_type {
LOGGER_SLAB_MOVE,
LOGGER_CONNECTION_NEW,
LOGGER_CONNECTION_CLOSE,
+ LOGGER_DELETIONS,
#ifdef EXTSTORE
LOGGER_EXTSTORE_WRITE,
LOGGER_COMPACT_START,
@@ -108,6 +109,14 @@ struct logentry_item_store {
char key[];
};
+struct logentry_deletion {
+ int nbytes;
+ int cmd;
+ uint8_t nkey;
+ uint8_t clsid;
+ char key[];
+};
+
struct logentry_conn_event {
int transport;
int reason;
@@ -164,6 +173,7 @@ struct _logentry {
#define LOG_PROXYREQS (1<<10) /* command logs from proxy */
#define LOG_PROXYEVENTS (1<<11) /* error log stream from proxy */
#define LOG_PROXYUSER (1<<12) /* user generated logs from proxy */
+#define LOG_DELETIONS (1<<13) /* see whats deleted */
typedef struct _logger {
struct _logger *prev;
diff --git a/memcached.h b/memcached.h
index 7ebe65f..3c54931 100644
--- a/memcached.h
+++ b/memcached.h
@@ -272,6 +272,9 @@ enum close_reasons {
#define CAS_ALLOW_STALE true
#define CAS_NO_STALE false
+#define LOG_TYPE_DELETE 1
+#define LOG_TYPE_META_DELETE 2
+
enum store_item_type {
NOT_STORED=0, STORED, EXISTS, NOT_FOUND, TOO_LARGE, NO_MEMORY
};
diff --git a/proto_text.c b/proto_text.c
index 1f69ddc..58fd227 100644
--- a/proto_text.c
+++ b/proto_text.c
@@ -1684,6 +1684,7 @@ static void process_mdelete_command(conn *c, token_t *tokens, const size_t ntoke
c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
+ LOGGER_LOG(NULL, LOG_DELETIONS, LOGGER_DELETIONS, it, LOG_TYPE_META_DELETE);
do_item_unlink(it, hv);
STORAGE_delete(c->thread->storage, it);
if (c->noreply)
@@ -2176,7 +2177,7 @@ static void process_delete_command(conn *c, token_t *tokens, const size_t ntoken
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
-
+ LOGGER_LOG(NULL, LOG_DELETIONS, LOGGER_DELETIONS, it, LOG_TYPE_DELETE);
do_item_unlink(it, hv);
STORAGE_delete(c->thread->storage, it);
do_item_remove(it); /* release our reference */
@@ -2322,6 +2323,8 @@ static void process_watch_command(conn *c, token_t *tokens, const size_t ntokens
f |= LOG_PROXYEVENTS;
} else if ((strcmp(tokens[x].value, "proxyuser") == 0)) {
f |= LOG_PROXYUSER;
+ } else if ((strcmp(tokens[x].value, "deletions") == 0)) {
+ f |= LOG_DELETIONS;
} else {
out_string(c, "ERROR");
return;
diff --git a/t/watcher.t b/t/watcher.t
index f977c2a..09800a9 100755
--- a/t/watcher.t
+++ b/t/watcher.t
@@ -10,7 +10,7 @@ use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
-plan tests => 30;
+plan tests => 43;
my $server = new_memcached('-m 60 -o watcher_logbuf_size=8');
my $client = $server->sock;
@@ -235,3 +235,50 @@ SKIP: {
is($watchresult, "CLIENT_ERROR watch commands not allowed\r\n", "attempted watch gave client error with no_watch option set");
}
+
+
+# test delete/meta-delete with value sizes
+{
+ my $watcher = $server->new_sock;
+ print $watcher "watch deletions\n";
+ is(<$watcher>, "OK\r\n", "deletions watcher enabled");
+
+ print $client "set vfoo 0 0 4\r\nvbar\r\n";
+ is(<$client>, "STORED\r\n", "stored the key");
+
+ # wouldn't be logged to watcher
+ print $client "md non-existing-key\r\n";
+ is(<$client>, "NF\r\n", "non-existing key can't be deleted");
+
+ print $client "delete vfoo\r\n";
+ is(<$client>, "DELETED\r\n", "key was deleted");
+
+ like(<$watcher>, qr/ts=\d+\.\d+\ gid=\d+ type=deleted key=vfoo cmd=delete .+ size=4/,
+ "delete command logged with correct size");
+
+ print $client "set vfoo 0 0 4\r\nvbar\r\n";
+ is(<$client>, "STORED\r\n", "stored the key");
+
+ print $client "md vfoo\r\n";
+ is(<$client>, "HD\r\n", "key was deleted");
+
+ like(<$watcher>, qr/ts=\d+\.\d+\ gid=\d+ type=deleted key=vfoo cmd=md .+ size=4/,
+ "meta-delete command logged with correct size");
+
+ print $client "set vfoo 0 0 4\r\nvbar\r\n";
+ is(<$client>, "STORED\r\n", "stored the key");
+
+ # shouldn't result in log line
+ print $client "md vfoo I\r\n";
+ is(<$client>, "HD\r\n", "key was marked stale");
+
+ print $client "mg vfoo\r\n";
+ is(<$client>, "HD X W\r\n", "mg shows key is stale and won recache");
+
+ # now do a delete and it will show up in the log, explicitly use delete instead of md to check the cmd= pair in log
+ print $client "delete vfoo\r\n";
+ is(<$client>, "DELETED\r\n", "key was deleted");
+
+ like(<$watcher>, qr/ts=\d+\.\d+\ gid=\d+ type=deleted key=vfoo cmd=delete .+ size=4/,
+ "delete command logged with correct size");
+}