|
Packit |
4e8bc4 |
#!/usr/bin/perl
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
use strict;
|
|
Packit |
4e8bc4 |
use warnings;
|
|
Packit |
4e8bc4 |
use Test::More;
|
|
Packit |
4e8bc4 |
use FindBin qw($Bin);
|
|
Packit |
4e8bc4 |
use lib "$Bin/lib";
|
|
Packit |
4e8bc4 |
use MemcachedTest;
|
|
Packit |
4e8bc4 |
use Data::Dumper qw/Dumper/;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
my $ext_path;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (!supports_extstore()) {
|
|
Packit |
4e8bc4 |
plan skip_all => 'extstore not enabled';
|
|
Packit |
4e8bc4 |
exit 0;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
$ext_path = "/tmp/extstore.$$";
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path:64m,slab_automove=0,ext_compact_under=1");
|
|
Packit |
4e8bc4 |
my $sock = $server->sock;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# Wait until all items have flushed
|
|
Packit |
4e8bc4 |
sub wait_for_ext {
|
|
Packit |
4e8bc4 |
my $target = shift || 0;
|
|
Packit |
4e8bc4 |
my $sum = $target + 1;
|
|
Packit |
4e8bc4 |
while ($sum > $target) {
|
|
Packit |
4e8bc4 |
my $s = mem_stats($sock, "items");
|
|
Packit |
4e8bc4 |
$sum = 0;
|
|
Packit |
4e8bc4 |
for my $key (keys %$s) {
|
|
Packit |
4e8bc4 |
if ($key =~ m/items:(\d+):number/) {
|
|
Packit |
4e8bc4 |
# Ignore classes which can contain extstore items
|
|
Packit |
4e8bc4 |
next if $1 < 3;
|
|
Packit |
4e8bc4 |
$sum += $s->{$key};
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
sleep 1 if $sum > $target;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
my $value;
|
|
Packit |
4e8bc4 |
{
|
|
Packit |
4e8bc4 |
my @chars = ("C".."Z");
|
|
Packit |
4e8bc4 |
for (1 .. 20000) {
|
|
Packit |
4e8bc4 |
$value .= $chars[rand @chars];
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# fill a small object
|
|
Packit |
4e8bc4 |
print $sock "set foo 0 0 2\r\nhi\r\n";
|
|
Packit |
4e8bc4 |
is(scalar <$sock>, "STORED\r\n", "stored small value");
|
|
Packit |
4e8bc4 |
# fetch
|
|
Packit |
4e8bc4 |
mem_get_is($sock, "foo", "hi");
|
|
Packit |
4e8bc4 |
# check extstore counters
|
|
Packit |
4e8bc4 |
{
|
|
Packit |
4e8bc4 |
my $stats = mem_stats($sock);
|
|
Packit |
4e8bc4 |
is($stats->{extstore_objects_written}, 0);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
# fill some larger objects
|
|
Packit |
4e8bc4 |
{
|
|
Packit |
4e8bc4 |
# set one canary value for later
|
|
Packit |
4e8bc4 |
print $sock "set canary 0 0 20000 noreply\r\n$value\r\n";
|
|
Packit |
4e8bc4 |
my $keycount = 1000;
|
|
Packit |
4e8bc4 |
for (1 .. $keycount) {
|
|
Packit |
4e8bc4 |
print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n";
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
# wait for a flush
|
|
Packit |
4e8bc4 |
wait_for_ext();
|
|
Packit |
4e8bc4 |
# fetch
|
|
Packit |
4e8bc4 |
# TODO: Fetch back all values
|
|
Packit |
4e8bc4 |
mem_get_is($sock, "nfoo1", $value);
|
|
Packit |
4e8bc4 |
# check extstore counters
|
|
Packit |
4e8bc4 |
my $stats = mem_stats($sock);
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read');
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# Remove half of the keys for the next test.
|
|
Packit |
4e8bc4 |
for (1 .. $keycount) {
|
|
Packit |
4e8bc4 |
next unless $_ % 2 == 0;
|
|
Packit |
4e8bc4 |
print $sock "delete nfoo$_ noreply\r\n";
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
my $stats2 = mem_stats($sock);
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_bytes_used}, '>', $stats2->{extstore_bytes_used},
|
|
Packit |
4e8bc4 |
'bytes used dropped after deletions');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_objects_used}, '>', $stats2->{extstore_objects_used},
|
|
Packit |
4e8bc4 |
'objects used dropped after deletions');
|
|
Packit |
4e8bc4 |
is($stats2->{badcrc_from_extstore}, 0, 'CRC checks successful');
|
|
Packit |
4e8bc4 |
is($stats2->{miss_from_extstore}, 0, 'no misses');
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# delete the rest
|
|
Packit |
4e8bc4 |
for (1 .. $keycount) {
|
|
Packit |
4e8bc4 |
next unless $_ % 2 == 1;
|
|
Packit |
4e8bc4 |
print $sock "delete nfoo$_ noreply\r\n";
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# fill to eviction
|
|
Packit |
4e8bc4 |
{
|
|
Packit |
4e8bc4 |
my $keycount = 4000;
|
|
Packit |
4e8bc4 |
for (1 .. $keycount) {
|
|
Packit |
4e8bc4 |
print $sock "set mfoo$_ 0 0 20000 noreply\r\n$value\r\n";
|
|
Packit |
4e8bc4 |
# wait to avoid evictions
|
|
Packit |
4e8bc4 |
wait_for_ext(500) if ($_ % 2000 == 0);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
# because item_age is set to 2s
|
|
Packit |
4e8bc4 |
wait_for_ext();
|
|
Packit |
4e8bc4 |
my $stats = mem_stats($sock);
|
|
Packit |
4e8bc4 |
is($stats->{evictions}, 0, 'no evictions');
|
|
Packit |
4e8bc4 |
is($stats->{miss_from_extstore}, 0, 'no misses');
|
|
Packit |
4e8bc4 |
# FIXME: test is flaky; something can rescue the canary because of a race
|
|
Packit |
4e8bc4 |
# condition. might need to roundtrip twice or disable compaction?
|
|
Packit |
4e8bc4 |
#mem_get_is($sock, "canary", undef);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# check counters
|
|
Packit |
4e8bc4 |
$stats = mem_stats($sock);
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_page_evictions}, '>', 0, 'at least one page evicted');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_objects_evicted}, '>', 0, 'at least one object evicted');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_bytes_evicted}, '>', 0, 'some bytes evicted');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_pages_free}, '<', 2, 'few pages are free');
|
|
Packit |
4e8bc4 |
#is($stats->{miss_from_extstore}, 1, 'exactly one miss');
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# refresh some keys so rescues happen while drop_unread == 1.
|
|
Packit |
4e8bc4 |
for (1 .. $keycount / 2) {
|
|
Packit |
4e8bc4 |
next unless $_ % 2 == 1;
|
|
Packit |
4e8bc4 |
# Need to be fetched twice in order to bump
|
|
Packit |
4e8bc4 |
print $sock "touch mfoo$_ 0 noreply\r\n";
|
|
Packit |
4e8bc4 |
print $sock "touch mfoo$_ 0 noreply\r\n";
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
print $sock "extstore drop_unread 1\r\n";
|
|
Packit |
4e8bc4 |
my $res = <$sock>;
|
|
Packit |
4e8bc4 |
print $sock "extstore max_frag 0\r\n";
|
|
Packit |
4e8bc4 |
$res = <$sock>;
|
|
Packit |
4e8bc4 |
print $sock "extstore compact_under 4\r\n";
|
|
Packit |
4e8bc4 |
$res = <$sock>;
|
|
Packit |
4e8bc4 |
print $sock "extstore drop_under 3\r\n";
|
|
Packit |
4e8bc4 |
$res = <$sock>;
|
|
Packit |
4e8bc4 |
for (1 .. $keycount) {
|
|
Packit |
4e8bc4 |
next unless $_ % 2 == 0;
|
|
Packit |
4e8bc4 |
print $sock "delete mfoo$_ noreply\r\n";
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
sleep 4;
|
|
Packit |
4e8bc4 |
$stats = mem_stats($sock);
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_pages_free}, '>', 0, 'some pages now free');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened');
|
|
Packit |
4e8bc4 |
cmp_ok($stats->{extstore_compact_skipped}, '>', 0, 'some compaction skips happened');
|
|
Packit |
4e8bc4 |
print $sock "extstore drop_unread 0\r\n";
|
|
Packit |
4e8bc4 |
$res = <$sock>;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# attempt to incr/decr/append/prepend or chunk objects that were sent to disk.
|
|
Packit |
4e8bc4 |
{
|
|
Packit |
4e8bc4 |
my $keycount = 100;
|
|
Packit |
4e8bc4 |
for (1 .. $keycount) {
|
|
Packit |
4e8bc4 |
print $sock "set bfoo$_ 0 0 20000 noreply\r\n$value\r\n";
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
wait_for_ext();
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# incr should be blocked.
|
|
Packit |
4e8bc4 |
print $sock "incr bfoo1 1\r\n";
|
|
Packit |
4e8bc4 |
is(scalar <$sock>, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 'incr fails');
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
# append/prepend *could* work, but it would require pulling the item back in.
|
|
Packit |
4e8bc4 |
print $sock "append bfoo1 0 0 2\r\nhi\r\n";
|
|
Packit |
4e8bc4 |
is(scalar <$sock>, "NOT_STORED\r\n", 'append fails');
|
|
Packit |
4e8bc4 |
print $sock "prepend bfoo1 0 0 2\r\nhi\r\n";
|
|
Packit |
4e8bc4 |
is(scalar <$sock>, "NOT_STORED\r\n", 'prepend fails');
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
done_testing();
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
END {
|
|
Packit |
4e8bc4 |
unlink $ext_path if $ext_path;
|
|
Packit |
4e8bc4 |
}
|