A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 #!/usr/bin/perl 2 3 use strict; 4 use warnings; 5 use Test::More; 6 use FindBin qw($Bin); 7 use lib "$Bin/lib"; 8 use MemcachedTest; 9 use Data::Dumper qw/Dumper/; 10 11 my $ext_path; 12 13 if (!supports_extstore()) { 14 plan skip_all => 'extstore not enabled'; 15 exit 0; 16 } 17 18 $ext_path = "/tmp/extstore.$$"; 19 20 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,ext_max_sleep=100000"); 21 my $sock = $server->sock; 22 23 eval { 24 my $server = new_memcached("-o ext_path=$ext_path:64m"); 25 }; 26 ok($@, "failed to start a second server with the same file path"); 27 28 # Wait until all items have flushed 29 sub wait_for_ext { 30 my $target = shift || 0; 31 my $sum = $target + 1; 32 while ($sum > $target) { 33 my $s = mem_stats($sock, "items"); 34 $sum = 0; 35 for my $key (keys %$s) { 36 if ($key =~ m/items:(\d+):number/) { 37 # Ignore classes which can contain extstore items 38 next if $1 < 3; 39 $sum += $s->{$key}; 40 } 41 } 42 sleep 1 if $sum > $target; 43 } 44 } 45 46 my $value; 47 { 48 my @chars = ("C".."Z"); 49 for (1 .. 20000) { 50 $value .= $chars[rand @chars]; 51 } 52 } 53 54 # fill a small object 55 print $sock "set foo 0 0 2\r\nhi\r\n"; 56 is(scalar <$sock>, "STORED\r\n", "stored small value"); 57 # fetch 58 mem_get_is($sock, "foo", "hi"); 59 # check extstore counters 60 { 61 my $stats = mem_stats($sock); 62 is($stats->{extstore_objects_written}, 0); 63 } 64 # fill some larger objects 65 { 66 # set one canary value for later 67 print $sock "set canary 0 0 20000 noreply\r\n$value\r\n"; 68 my $keycount = 1000; 69 for (1 .. $keycount) { 70 print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; 71 } 72 # wait for a flush 73 wait_for_ext(); 74 # fetch 75 # TODO: Fetch back all values 76 mem_get_is($sock, "nfoo1", $value); 77 # check extstore counters 78 my $stats = mem_stats($sock); 79 cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); 80 cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); 81 cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); 82 cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); 83 cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); 84 cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); 85 86 # Remove half of the keys for the next test. 87 for (1 .. $keycount) { 88 next unless $_ % 2 == 0; 89 print $sock "delete nfoo$_ noreply\r\n"; 90 } 91 92 my $stats2 = mem_stats($sock); 93 cmp_ok($stats->{extstore_bytes_used}, '>', $stats2->{extstore_bytes_used}, 94 'bytes used dropped after deletions'); 95 cmp_ok($stats->{extstore_objects_used}, '>', $stats2->{extstore_objects_used}, 96 'objects used dropped after deletions'); 97 is($stats2->{badcrc_from_extstore}, 0, 'CRC checks successful'); 98 is($stats2->{miss_from_extstore}, 0, 'no misses'); 99 100 # delete the rest 101 for (1 .. $keycount) { 102 next unless $_ % 2 == 1; 103 print $sock "delete nfoo$_ noreply\r\n"; 104 } 105 } 106 107 # fill to eviction 108 { 109 my $keycount = 4000; 110 for (1 .. $keycount) { 111 print $sock "set mfoo$_ 0 0 20000 noreply\r\n$value\r\n"; 112 # wait to avoid evictions 113 wait_for_ext(500) if ($_ % 2000 == 0); 114 } 115 # because item_age is set to 2s 116 wait_for_ext(); 117 my $stats = mem_stats($sock); 118 is($stats->{evictions}, 0, 'no evictions'); 119 is($stats->{miss_from_extstore}, 0, 'no misses'); 120 # FIXME: test is flaky; something can rescue the canary because of a race 121 # condition. might need to roundtrip twice or disable compaction? 122 #mem_get_is($sock, "canary", undef); 123 124 # check counters 125 $stats = mem_stats($sock); 126 cmp_ok($stats->{extstore_page_evictions}, '>', 0, 'at least one page evicted'); 127 cmp_ok($stats->{extstore_objects_evicted}, '>', 0, 'at least one object evicted'); 128 cmp_ok($stats->{extstore_bytes_evicted}, '>', 0, 'some bytes evicted'); 129 cmp_ok($stats->{extstore_pages_free}, '<', 2, 'few pages are free'); 130 #is($stats->{miss_from_extstore}, 1, 'exactly one miss'); 131 132 # refresh some keys so rescues happen while drop_unread == 1. 133 for (1 .. $keycount / 2) { 134 next unless $_ % 2 == 1; 135 # Need to be fetched twice in order to bump 136 print $sock "touch mfoo$_ 0 noreply\r\n"; 137 print $sock "touch mfoo$_ 0 noreply\r\n"; 138 } 139 print $sock "extstore drop_unread 1\r\n"; 140 my $res = <$sock>; 141 print $sock "extstore max_frag 0\r\n"; 142 $res = <$sock>; 143 print $sock "extstore compact_under 4\r\n"; 144 $res = <$sock>; 145 print $sock "extstore drop_under 3\r\n"; 146 $res = <$sock>; 147 for (1 .. $keycount) { 148 next unless $_ % 2 == 0; 149 print $sock "delete mfoo$_ noreply\r\n"; 150 } 151 152 sleep 4; 153 $stats = mem_stats($sock); 154 cmp_ok($stats->{extstore_pages_free}, '>', 0, 'some pages now free'); 155 cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened'); 156 cmp_ok($stats->{extstore_compact_skipped}, '>', 0, 'some compaction skips happened'); 157 print $sock "extstore drop_unread 0\r\n"; 158 $res = <$sock>; 159 } 160 161 # attempt to incr/decr/append/prepend or chunk objects that were sent to disk. 162 { 163 my $keycount = 100; 164 for (1 .. $keycount) { 165 print $sock "set bfoo$_ 0 0 20000 noreply\r\n$value\r\n"; 166 } 167 wait_for_ext(); 168 169 # incr should be blocked. 170 print $sock "incr bfoo1 1\r\n"; 171 is(scalar <$sock>, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 'incr fails'); 172 173 # append/prepend *could* work, but it would require pulling the item back in. 174 print $sock "append bfoo1 0 0 2\r\nhi\r\n"; 175 is(scalar <$sock>, "NOT_STORED\r\n", 'append fails'); 176 print $sock "prepend bfoo1 0 0 2\r\nhi\r\n"; 177 is(scalar <$sock>, "NOT_STORED\r\n", 'prepend fails'); 178 } 179 180 done_testing(); 181 182 END { 183 unlink $ext_path if $ext_path; 184 }