Skip to content

Commit 13340b1

Browse files
elfchiefdormando
authored andcommitted
Stats and tests for 'refhang' problem
Add "reflocked" counter to "stats" and "stats items". Not sure if this is a good name for that or not. Add tests to verify a fully 'locked' LRU correctly reports oom
1 parent f2a4e5b commit 13340b1

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

items.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ typedef struct {
2929
uint64_t expired_unfetched;
3030
uint64_t evicted_unfetched;
3131
uint64_t crawler_reclaimed;
32+
uint64_t reflocked;
3233
} itemstats_t;
3334

3435
static item *heads[LARGEST_ID];
@@ -138,6 +139,7 @@ item *do_item_alloc(char *key, const size_t nkey, const int flags,
138139
tries_reflocked--;
139140
tries++;
140141
refcount_decr(&search->refcount);
142+
itemstats[id].reflocked++;
141143
/* Old rare bug could cause a refcount leak. We haven't seen
142144
* it in years, but we leave this code in to prevent failures
143145
* just in case */
@@ -475,6 +477,7 @@ void do_item_stats_totals(ADD_STAT add_stats, void *c) {
475477
totals.evicted += itemstats[i].evicted;
476478
totals.reclaimed += itemstats[i].reclaimed;
477479
totals.crawler_reclaimed += itemstats[i].crawler_reclaimed;
480+
totals.reflocked += itemstats[i].reflocked;
478481
}
479482
APPEND_STAT("expired_unfetched", "%llu",
480483
(unsigned long long)totals.expired_unfetched);
@@ -486,6 +489,8 @@ void do_item_stats_totals(ADD_STAT add_stats, void *c) {
486489
(unsigned long long)totals.reclaimed);
487490
APPEND_STAT("crawler_reclaimed", "%llu",
488491
(unsigned long long)totals.crawler_reclaimed);
492+
APPEND_STAT("reflocked", "%llu",
493+
(unsigned long long)totals.reflocked);
489494
}
490495

491496
void do_item_stats(ADD_STAT add_stats, void *c) {
@@ -520,6 +525,8 @@ void do_item_stats(ADD_STAT add_stats, void *c) {
520525
"%llu", (unsigned long long)itemstats[i].evicted_unfetched);
521526
APPEND_NUM_FMT_STAT(fmt, i, "crawler_reclaimed",
522527
"%llu", (unsigned long long)itemstats[i].crawler_reclaimed);
528+
APPEND_NUM_FMT_STAT(fmt, i, "reflocked",
529+
"%llu", (unsigned long long)itemstats[i].reflocked);
523530
}
524531
}
525532

t/refhang.t

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use strict;
44
use warnings;
55

6-
use Test::More tests => 240;
6+
use Test::More tests => 266;
77

88
use FindBin qw($Bin);
99
use lib "$Bin/lib";
@@ -12,22 +12,30 @@ use MemcachedTest;
1212
# start up a server with 10 maximum connections
1313
my $server = new_memcached("-m 6");
1414
my $sock = $server->sock;
15-
my $hangsock = $server->new_sock;;
15+
my $hangsock = $server->new_sock;
16+
my $hangsock2 = $server->new_sock;
1617
my $value = "B"x66560;
1718
my $key = 0;
1819

1920
# These aren't set to expire.
2021
my $mget = '';
22+
my $mget_all = '';
2123
for ($key = 0; $key < 120; $key++) {
2224
$mget .= "key$key " if $key < 115;
25+
$mget_all .= "key$key ";
2326
print $sock "set key$key 0 0 66560\r\n$value\r\n";
2427
is(scalar <$sock>, "STORED\r\n", "stored key$key");
2528
}
2629
chop $mget;
2730

2831
my $stats = mem_stats($sock, "items");
29-
my $evicted = $stats->{"items:31:evicted"};
30-
isnt($evicted, "0", "check evicted");
32+
isnt($stats->{"items:31:evicted"}, "0", "check evicted");
33+
34+
my $reflocked = $stats->{"items:31:reflocked"};
35+
is($reflocked, "0", "check no slab reflocked");
36+
37+
$stats = mem_stats($sock);
38+
is($stats->{"reflocked"}, "0", "check no total reflocked");
3139

3240
# Don't intend to read the results, need to fill the socket.
3341
# TODO: This test would be smarter if we cranked down the socket buffers
@@ -36,6 +44,33 @@ print $hangsock "get $mget\r\n";
3644
#sleep 8;
3745
# Now we try a bunch of sets again, and see if they start coming back as OOM's
3846
for ($key = 121; $key < 240; $key++) {
47+
$mget_all .= "key$key ";
3948
print $sock "set key$key 0 0 66560\r\n$value\r\n";
4049
is(scalar <$sock>, "STORED\r\n", "stored key$key");
4150
}
51+
52+
$stats = mem_stats($sock, "items");
53+
is($stats->{"items:31:outofmemory"}, "0", "check no oom");
54+
isnt($stats->{"items:31:reflocked"}, "0", "count reflocked");
55+
56+
$stats = mem_stats($sock);
57+
isnt($stats->{"reflocked"}, "0", "count total reflocked");
58+
59+
# Clear out all that 'hung' traffic
60+
while(<$hangsock> !~ /END/) { };
61+
62+
# Make sure we get a oom when the entire world is refcounted
63+
print $hangsock "get $mget_all\r\n";
64+
65+
# Get all our keys in a different order to make sure some of the cache isn't
66+
# free just because it made it to the tcp buffer
67+
my $revkeys = join(" ", reverse(split(" ", $mget_all)));
68+
print $hangsock2 "get $revkeys\r\n";
69+
70+
for ($key = 240; $key < 260; $key++) {
71+
print $sock "set key$key 0 0 66560\r\n$value\r\n";
72+
is(scalar <$sock>, "SERVER_ERROR out of memory storing object\r\n", "oom fully reflocked");
73+
}
74+
75+
$stats = mem_stats($sock, "items");
76+
isnt($stats->{"items:31:outofmemory"}, "0", "count reflocked oom");

t/stats.t

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ my $sock = $server->sock;
2323
## STAT curr_connections 10
2424
## STAT total_connections 11
2525
## STAT connection_structures 11
26+
## STAT reserved_fds 20
2627
## STAT cmd_get 0
2728
## STAT cmd_set 0
2829
## STAT cmd_flush 0
30+
## STAT cmd_touch 0
2931
## STAT get_hits 0
3032
## STAT get_misses 0
3133
## STAT delete_misses 0
@@ -37,6 +39,8 @@ my $sock = $server->sock;
3739
## STAT cas_misses 0
3840
## STAT cas_hits 0
3941
## STAT cas_badval 0
42+
## STAT touch_hits 0
43+
## STAT touch_misses 0
4044
## STAT auth_cmds 0
4145
## STAT auth_unknowns 0
4246
## STAT bytes_read 7
@@ -46,24 +50,31 @@ my $sock = $server->sock;
4650
## STAT listen_disabled_num 0
4751
## STAT threads 4
4852
## STAT conn_yields 0
53+
## STAT hash_power_level 16
54+
## STAT hash_bytes 524288
55+
## STAT hash_is_expanding 0
56+
## STAT malloc_fails 0
4957
## STAT bytes 0
5058
## STAT curr_items 0
5159
## STAT total_items 0
60+
## STAT expired_unfetched 0
61+
## STAT evicted_unfetched 0
5262
## STAT evictions 0
5363
## STAT reclaimed 0
54-
64+
## STAT crawler_reclaimed 0
65+
## STAT reflocked 0
5566
# note that auth stats are tested in auth specfic tests
5667

5768

5869
my $stats = mem_stats($sock);
5970

6071
# Test number of keys
61-
is(scalar(keys(%$stats)), 50, "50 stats values");
72+
is(scalar(keys(%$stats)), 51, "51 stats values");
6273

6374
# Test initial state
6475
foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses
6576
bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits
66-
decr_misses listen_disabled_num)) {
77+
decr_misses listen_disabled_num reflocked)) {
6778
is($stats->{$key}, 0, "initial $key is zero");
6879
}
6980
is($stats->{accepting_conns}, 1, "initial accepting_conns is one");
@@ -188,6 +199,7 @@ is(0, $stats->{'cas_hits'});
188199
is(0, $stats->{'cas_badval'});
189200
is(0, $stats->{'evictions'});
190201
is(0, $stats->{'reclaimed'});
202+
is(0, $stats->{'reflocked'});
191203

192204
print $sock "flush_all\r\n";
193205
is(scalar <$sock>, "OK\r\n", "flushed");

0 commit comments

Comments
 (0)