localmemcache 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/site/index.html +124 -38
- data/src/lmc_common.c +4 -3
- data/src/lmc_valloc.c +13 -6
- data/src/lmc_valloc.h +1 -0
- data/src/localmemcache.c +1 -0
- data/src/ruby-binding/localmemcache.rb +5 -1
- data/src/ruby-binding/rblocalmemcache.c +59 -5
- data/src/tests/bench.rb +1 -1
- data/src/tests/console +6 -0
- data/src/tests/console.rb +4 -0
- data/src/tests/crash.rb +1 -1
- data/src/tests/iter.rb +1 -1
- data/src/tests/lmc.rb +29 -10
- data/src/tests/run-all-tests +2 -0
- data/src/tests/ttlmc-consistency +11 -0
- data/src/tests/ttlmc-consistency.rb +57 -0
- data/src/tests/ttlmc.rb +5 -5
- metadata +6 -3
- data/site/style.css +0 -37
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.4
|
data/site/index.html
CHANGED
@@ -1,14 +1,96 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
3
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
4
|
-
<title>Localmemcache</title>
|
5
|
-
|
4
|
+
<title>Localmemcache: mmap -> lmc_valloc -> hashtable. BAM database</title>
|
5
|
+
<script type="text/javascript">
|
6
|
+
// <![CDATA[
|
7
|
+
function show_complete_history() {
|
8
|
+
var e = document.getElementById('old-news')
|
9
|
+
e.style.display = e.style.display == "none" ? "block" : "none";
|
10
|
+
return false;
|
11
|
+
}
|
12
|
+
// ]]
|
13
|
+
</script>
|
14
|
+
|
15
|
+
<style type="text/css">
|
16
|
+
body {
|
17
|
+
background: #FFF;
|
18
|
+
color: #333;
|
19
|
+
font-family: "Helvetica Neue", Helvetica, "Trebuchet MS", "Lucida Grande", Verdana, sans-serif;
|
20
|
+
margin: 0;
|
21
|
+
font-size: 17pt;
|
22
|
+
line-height: 1.3em;
|
23
|
+
}
|
24
|
+
|
25
|
+
h1, h2, h3, h4 {
|
26
|
+
color: #0;
|
27
|
+
padding: 0;
|
28
|
+
margin: 1em 0;
|
29
|
+
}
|
30
|
+
|
31
|
+
#opener-background {
|
32
|
+
background-color: #333;
|
33
|
+
padding-bottom: 10px;
|
34
|
+
}
|
35
|
+
|
36
|
+
#opener {
|
37
|
+
width: 1024px;
|
38
|
+
}
|
39
|
+
|
40
|
+
#opener, #opener * {
|
41
|
+
font: 40pt/44pt "Helvetica Neue", Helvetica, Arial, sans-serif;
|
42
|
+
font-weight: bold;
|
43
|
+
letter-spacing: -0.75pt;
|
44
|
+
margin: auto ;
|
45
|
+
color: #aaa;
|
46
|
+
background-color: #333;
|
47
|
+
}
|
48
|
+
|
49
|
+
#opener em {
|
50
|
+
color: #fff;
|
51
|
+
}
|
52
|
+
|
53
|
+
#content em {
|
54
|
+
color: #aaa;
|
55
|
+
font: 50pt/40pt "Helvetica Neue", Helvetica, Arial, sans-serif;
|
56
|
+
letter-spacing: -0.75pt;
|
57
|
+
font-weight: bold;
|
58
|
+
}
|
59
|
+
|
60
|
+
#opener #title {
|
61
|
+
line-height: 1.9em;
|
62
|
+
}
|
63
|
+
|
64
|
+
pre, code {
|
65
|
+
font-family: Courier;
|
66
|
+
font-size: 17pt;
|
67
|
+
overflow: auto;
|
68
|
+
}
|
69
|
+
|
70
|
+
a { color: #0088FF; text-decoration: none }
|
71
|
+
a:hover { color: #fff; background-color: #0088FF }
|
72
|
+
|
73
|
+
#content {
|
74
|
+
margin: 0 auto ;
|
75
|
+
width: 1024px;
|
76
|
+
}
|
77
|
+
|
78
|
+
ol, ul { padding-left: 0; }
|
79
|
+
|
80
|
+
</style>
|
6
81
|
</head>
|
7
|
-
|
82
|
+
<body>
|
83
|
+
<div id="opener-background">
|
84
|
+
<div id="opener">
|
85
|
+
<div id="title">How would one design a database today?</div>
|
86
|
+
<table cellspacing=0 cellpadding=0 width="100%">
|
87
|
+
<tr><td><em>then</em></td><td>memory scarce, open, read, write</td></tr>
|
88
|
+
<tr><td><em>today</em></td><td>64bit, mmap, sparse files</td></tr>
|
89
|
+
</table>
|
90
|
+
</div>
|
91
|
+
</div>
|
8
92
|
|
9
93
|
<div id="content">
|
10
|
-
<h1>A persistent key-value database based on mmap()'ed shared memory</h1>
|
11
|
-
|
12
94
|
<p><b>Localmemcache</b> is a library for <b>C</b> and <b>Ruby</b> that aims
|
13
95
|
to provide
|
14
96
|
an interface similar to memcached but for accessing local data instead of
|
@@ -17,6 +99,17 @@ speed</b>.
|
|
17
99
|
Since version 0.3.0 it supports <b>persistence</b>, also making it <b>a fast
|
18
100
|
alternative to GDBM, Berkeley DB, and Tokyo Cabinet</b>.
|
19
101
|
|
102
|
+
<h2>Version 0.4.4: Bugfixes for OS X and Autorepair</h2>
|
103
|
+
|
104
|
+
Version 0.4.4 brings fixes for coredumps on OS X and bugs in the
|
105
|
+
autorepair (now also better covered by tests). <br>
|
106
|
+
New methods: <b>shm_status</b>, <b>has_key?</b><br>
|
107
|
+
(Thanks to <b>Max Schöfmann</b> and <b>Florian Dütsch</b> for feedback/bug reports.)
|
108
|
+
<p>
|
109
|
+
|
110
|
+
<a href="#" onclick="return show_complete_history()" />Previous Releases</a>
|
111
|
+
<div id='old-news' style='display:none'>
|
112
|
+
|
20
113
|
<h2>Version 0.4.3: Improving Iteration and bugfixes (2009-10-02)</h2>
|
21
114
|
|
22
115
|
Fixes by <b>Paul Mineiro</b> (thanks!):
|
@@ -26,26 +119,13 @@ faster</b>.</li>
|
|
26
119
|
<li><b>fix for leak</b> in local_memcache_free()</li>
|
27
120
|
<li><b>C API</b>: Some C API functions were not yet covered by tests. This
|
28
121
|
has been fixed.</li>
|
29
|
-
<p>
|
30
122
|
|
31
123
|
Other fixes:
|
32
124
|
<li><b>autorepair</b> fixed: Autorepair of namespaces did sometimes wrongly
|
33
125
|
report corrupt namespaces.
|
34
126
|
<li><b>:min_alloc_size</b> now also works on Ruby 1.9.</li>
|
35
127
|
|
36
|
-
|
37
|
-
<script type="text/javascript">
|
38
|
-
// <![CDATA[
|
39
|
-
function show_complete_history() {
|
40
|
-
var e = document.getElementById('old-news')
|
41
|
-
e.style.display = e.style.display == "none" ? "block" : "none";
|
42
|
-
return false;
|
43
|
-
}
|
44
|
-
// ]]
|
45
|
-
</script>
|
46
|
-
|
47
|
-
<a href="#" onclick="show_complete_history()" />Previous Releases</a>
|
48
|
-
<div id='old-news' style='display:none'><h2>Version 0.4.2: Improving Append Performance (2009-08-10)</h2>
|
128
|
+
<h2>Version 0.4.2: Improving Append Performance (2009-08-10)</h2>
|
49
129
|
|
50
130
|
In 0.4.2 the <b>:min_alloc_size</b> parameter was introduced to help with use
|
51
131
|
cases that intend to use a hash table with growing values. This is
|
@@ -54,24 +134,15 @@ with a large list of unusable free blocks. By setting the
|
|
54
134
|
<b>:min_alloc_size</b> parameter you help the allocator to plan better
|
55
135
|
ahead. (<a href="http://localmemcache.rubyforge.org/doc/classes/LocalMemCache.html#M000001">more</a>)
|
56
136
|
|
57
|
-
</
|
58
|
-
|
59
|
-
<h2>Key features as of 0.4.0 (2009-05-16)</h2>
|
137
|
+
<h2>Key features as of 0.4.4 (2009-11-10)</h2>
|
60
138
|
<li><a href="#performance">blazingly fast</a></li>
|
61
139
|
<li>a very simple API</li>
|
62
140
|
<li>persistent</li>
|
63
141
|
<li>parallel writes are supported by default</li>
|
64
142
|
<li>uses transactions internally to avoid data corruption</li>
|
65
|
-
<li>lightweight: the core library is just about <b>
|
143
|
+
<li>lightweight: the core library is just about <b>1500</b> lines of <b>C</b> code</li>
|
66
144
|
|
67
|
-
|
68
|
-
<li>a >=64bit Unix (32bit is possible but you'll run out of virtual address space quickly)</li>
|
69
|
-
<li>a file system that offers <a href="http://en.wikipedia.org/wiki/Sparse_file">sparse files</a></li>
|
70
|
-
Note for <b>OS X</b>: <b>OS X</b> disqualifies as <b>HFS+</b> doesn't
|
71
|
-
have sparse files and <b>sem_timedwait</b>() and <b>sem_getvalue</b>() aren't
|
72
|
-
supported as well.<br>
|
73
|
-
Note for <b>FreeBSD</b>: It has been reported that localmemcache
|
74
|
-
sometimes hangs there, it is not yet clear what the problem is.
|
145
|
+
</div>
|
75
146
|
|
76
147
|
<h2>Install</h2>
|
77
148
|
|
@@ -83,6 +154,16 @@ sometimes hangs there, it is not yet clear what the problem is.
|
|
83
154
|
|
84
155
|
If you just want to use the <b>C API</b>, download the .tar.gz from <a href="http://rubyforge.org/frs/?group_id=7925">here</a>.
|
85
156
|
|
157
|
+
<h2>Requirements</h2>
|
158
|
+
<li>a >=64bit Unix (32bit is possible but you'll run out of virtual address space quickly)</li>
|
159
|
+
<li>a file system that offers <a href="http://en.wikipedia.org/wiki/Sparse_file">sparse files</a></li>
|
160
|
+
Note for <b>OS X</b>: <b>OS X</b> disqualifies as <b>HFS+</b> doesn't
|
161
|
+
have sparse files and <b>sem_timedwait</b>() and <b>sem_getvalue</b>() aren't
|
162
|
+
supported as well.<br>
|
163
|
+
Note for <b>FreeBSD</b>: It has been reported that localmemcache
|
164
|
+
sometimes hangs there, it is not yet clear what the problem is.
|
165
|
+
|
166
|
+
|
86
167
|
<h2>Using</h2>
|
87
168
|
<b>API</b>: <a href="http://github.com/sck/localmemcache/blob/d72a5bd825b0fb573f1048ea9cae92d8c143525c/src/localmemcache.h">C</a>|<a href="http://localmemcache.rubyforge.org/doc/">Ruby</a><br>
|
88
169
|
<p><pre><code>require 'localmemcache'
|
@@ -130,6 +211,16 @@ data on disk is about <b>10</b>% slower than keeping them in memory in a
|
|
130
211
|
Ruby hash of strings. It's about <b>40</b>% faster than <b>Tokyo Cabinet</b>
|
131
212
|
(which offers features similar to <b>LocalMemCache</b>).
|
132
213
|
|
214
|
+
<h2>Who uses Localmemcache?</h2>
|
215
|
+
|
216
|
+
<a href="http://www.personifi.com">Personifi</a> use localmemcache to
|
217
|
+
serve billions of hits each month. Armin Roehrl: "we use
|
218
|
+
localmemcache because it solves one problem very well and we love it!"
|
219
|
+
|
220
|
+
<h2>Slides for my talk at the <a href="http://www.munich-on-rails.com/">Ruby on Rails Group Munich</a></h2>
|
221
|
+
|
222
|
+
Now available on github < <a href="http://cloud.github.com/downloads/sck/localmemcache/localmemcache.pdf">pdf</a> | <a href="http://cloud.github.com/downloads/sck/localmemcache/localmemcache.key">key</a> > (German)
|
223
|
+
|
133
224
|
<h2>Source code</h2>
|
134
225
|
|
135
226
|
<p>The source code is hosted on <a href="http://github.com/sck/localmemcache/tree/master">github</a>. It
|
@@ -140,18 +231,13 @@ can be retrieved by executing</p>
|
|
140
231
|
|
141
232
|
<h2>Caveats</h2>
|
142
233
|
|
143
|
-
<li>Localmemcache's .lmc files are not binary compatible
|
234
|
+
<li>Localmemcache's .lmc files are not binary compatible across different
|
235
|
+
CPU architectures, they are essentially
|
144
236
|
memory mapped c structs</li>
|
145
237
|
<li>Because of the convenient auto repair feature after a lock timeout,
|
146
238
|
localmemcache is allergic to SIGSTOP (If you manage to SIGSTOP a process
|
147
239
|
while localmemcache is currently possessing a lock, that is)</li>
|
148
240
|
|
149
|
-
<h2>Who uses Localmemcache?</h2>
|
150
|
-
|
151
|
-
<a href="http://www.personifi.com">Personifi</a> use localmemcache to
|
152
|
-
serve billions of hits each month. Armin Roehrl: "we use
|
153
|
-
localmemcache because it solves one problem very well and we love it!"
|
154
|
-
|
155
241
|
<h2>Tips for backups</h2>
|
156
242
|
|
157
243
|
Note that you cannot copy localmemcache's .lmc files while other
|
data/src/lmc_common.c
CHANGED
@@ -43,9 +43,10 @@ size_t lmc_test_valloc_fail(const char *file, int line, const char *function,
|
|
43
43
|
}
|
44
44
|
|
45
45
|
void lmc_clean_string(char *result, const char *source) {
|
46
|
-
size_t
|
47
|
-
|
48
|
-
|
46
|
+
size_t nn = strlen(source);
|
47
|
+
size_t n = nn;
|
48
|
+
if (n > 20) { n = 20; }
|
49
|
+
const char *s = source + nn - n;
|
49
50
|
char *d = result;
|
50
51
|
char ch;
|
51
52
|
for (; n--; d++, s++) {
|
data/src/lmc_valloc.c
CHANGED
@@ -39,7 +39,6 @@ void lmc_dump_chunk_brief(char *who, void *base, lmc_mem_chunk_descriptor_t* c)
|
|
39
39
|
printf("[%s] chunk %zd:\n", who, va_c);
|
40
40
|
}
|
41
41
|
|
42
|
-
|
43
42
|
void lmc_dump(void *base) {
|
44
43
|
lmc_mem_chunk_descriptor_t* c = md_first_free(base);
|
45
44
|
size_t free = 0;
|
@@ -85,6 +84,11 @@ int lmc_is_va_valid(void *base, size_t va) {
|
|
85
84
|
(base + md->total_size + sizeof(lmc_mem_descriptor_t)) < (void *)c);
|
86
85
|
}
|
87
86
|
|
87
|
+
size_t lmc_total_shm_size(void *base) {
|
88
|
+
lmc_mem_descriptor_t *md = base;
|
89
|
+
return md->total_size + sizeof(lmc_mem_descriptor_t);
|
90
|
+
}
|
91
|
+
|
88
92
|
lmc_mem_status_t lmc_status(void *base, char *where) {
|
89
93
|
lmc_mem_descriptor_t *md = base;
|
90
94
|
lmc_mem_chunk_descriptor_t* c = md_first_free(base);
|
@@ -93,6 +97,7 @@ lmc_mem_status_t lmc_status(void *base, char *where) {
|
|
93
97
|
size_t largest_chunk = 0;
|
94
98
|
long chunks = 0;
|
95
99
|
ms.total_mem = md->total_size;
|
100
|
+
ms.total_shm_size = lmc_total_shm_size(base);
|
96
101
|
while (c) {
|
97
102
|
if (!lmc_is_va_valid(base, (void *)c - base)) {
|
98
103
|
printf("[localmemcache] [%s] invalid pointer detected: %zd...\n", where,
|
@@ -379,11 +384,12 @@ int lmc_um_find_leaks(void *base, char *bf) {
|
|
379
384
|
size_t gap_count = 0;
|
380
385
|
size_t space = 0;
|
381
386
|
memset(&m, 0xFF, sizeof(m));
|
382
|
-
|
387
|
+
size_t tts = lmc_total_shm_size(md);
|
388
|
+
for (i = 0; i < tts; ++i) {
|
383
389
|
if (!gap) {
|
384
390
|
size_t *b = (void *)bf + i / 8;
|
385
|
-
size_t ee =
|
386
|
-
while (
|
391
|
+
size_t ee = tts - sizeof(size_t) * 8;
|
392
|
+
while (i < ee && *b == m) {
|
387
393
|
i += sizeof(size_t) * 8; b++;
|
388
394
|
}
|
389
395
|
}
|
@@ -417,10 +423,11 @@ int lmc_um_check_unmarked(void *base, char *bf, size_t va, size_t size) {
|
|
417
423
|
for (i = va; i < va + size; ++i) {
|
418
424
|
size_t *b = (void *)bf + i / 8;
|
419
425
|
size_t ee = end - sizeof(size_t) * 8;
|
420
|
-
while (
|
426
|
+
while (i < ee && *b == n) {
|
421
427
|
i += sizeof(size_t) * 8; b++;
|
422
428
|
}
|
423
429
|
if (lmc_um_getbit(bf, i) != 0) {
|
430
|
+
printf("i: %zd marked!\n", i);
|
424
431
|
return 0;
|
425
432
|
}
|
426
433
|
}
|
@@ -464,7 +471,7 @@ int lmc_um_mark_allocated(void *base, char *bf, size_t va) {
|
|
464
471
|
|
465
472
|
char *lmc_um_new_mem_usage_bitmap(void *base) {
|
466
473
|
lmc_mem_descriptor_t *md = base;
|
467
|
-
size_t ts = ((md
|
474
|
+
size_t ts = ((lmc_total_shm_size(md) + 7) / 8);
|
468
475
|
char *bf = calloc(1, ts);
|
469
476
|
size_t va = md->first_free;
|
470
477
|
if (!lmc_um_mark(base, bf, 0, sizeof(lmc_mem_descriptor_t))) goto failed;
|
data/src/lmc_valloc.h
CHANGED
data/src/localmemcache.c
CHANGED
@@ -155,6 +155,7 @@ local_memcache_t *local_memcache_create(const char *namespace,
|
|
155
155
|
char clean_ns[1024];
|
156
156
|
double s = size_mb == 0.0 ? 1024.0 : size_mb;
|
157
157
|
size_t si = s * 1024 * 1024;
|
158
|
+
if (si < 1024 * 1024) { si = 1024 * 1024; }
|
158
159
|
//printf("size: %f, s: %f, si: %zd\n", size_mb, s, si);
|
159
160
|
if (!lmc_namespace_or_filename((char *)clean_ns, namespace, filename, e))
|
160
161
|
return 0;
|
@@ -53,13 +53,17 @@ class LocalMemCache
|
|
53
53
|
o = { :size_mb => 0 }.update(options || {})
|
54
54
|
_new(o);
|
55
55
|
end
|
56
|
+
def has_key?(k) !get(k).nil? end
|
56
57
|
|
57
58
|
# <code>SharedObjectStorage</code> inherits from class LocalMemCache but
|
58
59
|
# stores Ruby objects as values instead of just strings (It still uses
|
59
60
|
# strings for the keys, though).
|
60
61
|
class SharedObjectStorage < LocalMemCache
|
62
|
+
alias __super_get get
|
61
63
|
def []=(key,val) super(key, Marshal.dump(val)) end
|
62
64
|
def [](key) v = super(key); v.nil? ? nil : Marshal.load(v) end
|
65
|
+
alias set []=
|
66
|
+
alias get []
|
63
67
|
def each_pair(&block)
|
64
68
|
super {|k, mv| block.call(k, Marshal.load(mv)) }
|
65
69
|
end
|
@@ -67,6 +71,6 @@ class LocalMemCache
|
|
67
71
|
rp = super
|
68
72
|
rp.nil? ? nil : [rp.first, Marshal.load(rp.last)]
|
69
73
|
end
|
74
|
+
def has_key?(k) !__super_get(k).nil? end
|
70
75
|
end
|
71
|
-
|
72
76
|
end
|
@@ -34,6 +34,24 @@ long long_value(VALUE i) { return NIL_P(i) ? 0 : NUM2LONG(rb_Integer(i)); }
|
|
34
34
|
double double_value(VALUE i) { return NUM2DBL(i); }
|
35
35
|
/* :nodoc: */
|
36
36
|
VALUE num2string(long i) { return rb_big2str(rb_int2big(i), 10); }
|
37
|
+
|
38
|
+
typedef struct {
|
39
|
+
char *cstr;
|
40
|
+
size_t len;
|
41
|
+
} lmc_rb_str_d_t;
|
42
|
+
|
43
|
+
/* :nodoc: */
|
44
|
+
void rstring_acquire(VALUE s, lmc_rb_str_d_t *d) {
|
45
|
+
if (NIL_P(s)) {
|
46
|
+
d->cstr = "nil";
|
47
|
+
d->len = 0;
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
VALUE v = TYPE(s) == T_STRING ? s : rb_funcall(s, rb_intern("to_s"), 0);
|
51
|
+
d->cstr = RSTRING_PTR(v);
|
52
|
+
d->len = RSTRING_LEN(v);
|
53
|
+
}
|
54
|
+
|
37
55
|
/* :nodoc: */
|
38
56
|
char *rstring_ptr(VALUE s) {
|
39
57
|
char* r = NIL_P(s) ? "nil" : RSTRING_PTR(rb_String(s));
|
@@ -218,8 +236,10 @@ static VALUE LocalMemCache__disable_test_crash(VALUE klass) {
|
|
218
236
|
*/
|
219
237
|
static VALUE LocalMemCache__get(VALUE obj, VALUE key) {
|
220
238
|
size_t l;
|
239
|
+
lmc_rb_str_d_t k;
|
240
|
+
rstring_acquire(key, &k);
|
221
241
|
const char* r = __local_memcache_get(get_LocalMemCache(obj),
|
222
|
-
|
242
|
+
k.cstr, k.len, &l);
|
223
243
|
VALUE rr = lmc_ruby_string2(r, l);
|
224
244
|
lmc_unlock_shm_region("local_memcache_get", get_LocalMemCache(obj));
|
225
245
|
return rr;
|
@@ -256,8 +276,10 @@ static VALUE LocalMemCache__random_pair(VALUE obj) {
|
|
256
276
|
*/
|
257
277
|
static VALUE LocalMemCache__set(VALUE obj, VALUE key, VALUE value) {
|
258
278
|
local_memcache_t *lmc = get_LocalMemCache(obj);
|
259
|
-
|
260
|
-
|
279
|
+
lmc_rb_str_d_t k, v;
|
280
|
+
rstring_acquire(key, &k);
|
281
|
+
rstring_acquire(value, &v);
|
282
|
+
if (!local_memcache_set(lmc, k.cstr, k.len, v.cstr, v.len)) {
|
261
283
|
rb_lmc_raise_exception(&lmc->error);
|
262
284
|
}
|
263
285
|
return Qnil;
|
@@ -283,8 +305,9 @@ static VALUE LocalMemCache__clear(VALUE obj) {
|
|
283
305
|
* Deletes key from hashtable. The key is converted to string.
|
284
306
|
*/
|
285
307
|
static VALUE LocalMemCache__delete(VALUE obj, VALUE key) {
|
286
|
-
|
287
|
-
|
308
|
+
lmc_rb_str_d_t k;
|
309
|
+
rstring_acquire(key, &k);
|
310
|
+
return local_memcache_delete(get_LocalMemCache(obj), k.cstr, k.len);
|
288
311
|
return Qnil;
|
289
312
|
}
|
290
313
|
|
@@ -414,6 +437,36 @@ static VALUE LocalMemCache__size(VALUE obj) {
|
|
414
437
|
return rb_int2big(ht->size);
|
415
438
|
}
|
416
439
|
|
440
|
+
/*
|
441
|
+
* call-seq:
|
442
|
+
* lmc.shm_status -> hash
|
443
|
+
*
|
444
|
+
* Some status information on the shared memory:
|
445
|
+
*
|
446
|
+
* :total_bytes # the total size of the shm in bytes
|
447
|
+
* :used_bytes # how many bytes are used in this shm
|
448
|
+
* # For exmpty namespaces this will reflect the amount
|
449
|
+
* # of memory used for the hash buckets and some other
|
450
|
+
* # administrative data structures.
|
451
|
+
* :free_bytes # how many bytes are free
|
452
|
+
*/
|
453
|
+
static VALUE LocalMemCache__shm_status(VALUE obj) {
|
454
|
+
VALUE hash = rb_hash_new();
|
455
|
+
|
456
|
+
local_memcache_t *lmc = get_LocalMemCache(obj);
|
457
|
+
if (!lmc_lock_shm_region("shm_status", lmc)) return Qnil;
|
458
|
+
lmc_mem_status_t ms = lmc_status(lmc->base, "shm_status");
|
459
|
+
if (!lmc_unlock_shm_region("shm_status", lmc)) return Qnil;
|
460
|
+
|
461
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("free_bytes")),
|
462
|
+
rb_int2big(ms.total_free_mem));
|
463
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("total_bytes")),
|
464
|
+
rb_int2big(ms.total_shm_size));
|
465
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("used_bytes")), rb_int2big(
|
466
|
+
ms.total_shm_size - ms.total_free_mem));
|
467
|
+
return hash;
|
468
|
+
}
|
469
|
+
|
417
470
|
/*
|
418
471
|
* internal, do not use
|
419
472
|
*/
|
@@ -500,6 +553,7 @@ void Init_rblocalmemcache() {
|
|
500
553
|
0);
|
501
554
|
rb_define_method(LocalMemCache, "close", LocalMemCache__close, 0);
|
502
555
|
rb_define_method(LocalMemCache, "size", LocalMemCache__size, 0);
|
556
|
+
rb_define_method(LocalMemCache, "shm_status", LocalMemCache__shm_status, 0);
|
503
557
|
rb_define_method(LocalMemCache, "check_consistency",
|
504
558
|
LocalMemCache__check_consistency, 0);
|
505
559
|
|
data/src/tests/bench.rb
CHANGED
@@ -7,7 +7,7 @@ require 'localmemcache'
|
|
7
7
|
Bacon.summary_on_exit
|
8
8
|
|
9
9
|
LocalMemCache.drop :namespace => "speed-comparison", :force => true
|
10
|
-
$lm2 = LocalMemCache.new :namespace=>"speed-comparison"
|
10
|
+
$lm2 = LocalMemCache.new :namespace=>"speed-comparison", :size_mb => 200
|
11
11
|
|
12
12
|
def compare_speed(n)
|
13
13
|
|
data/src/tests/console
ADDED
data/src/tests/crash.rb
CHANGED
data/src/tests/iter.rb
CHANGED
data/src/tests/lmc.rb
CHANGED
@@ -7,7 +7,7 @@ require 'localmemcache'
|
|
7
7
|
Bacon.summary_on_exit
|
8
8
|
|
9
9
|
LocalMemCache.drop :namespace => "test", :force => true
|
10
|
-
$lm = LocalMemCache.new :namespace=>"test", :size_mb =>
|
10
|
+
$lm = LocalMemCache.new :namespace=>"test", :size_mb => 2
|
11
11
|
|
12
12
|
LocalMemCache.drop :namespace => "test-small", :force => true
|
13
13
|
$lms = LocalMemCache.new :namespace=>"test-small", :size_mb => 1;
|
@@ -20,6 +20,14 @@ describe 'LocalMemCache' do
|
|
20
20
|
$lm.get("foo").should.equal "1"
|
21
21
|
end
|
22
22
|
|
23
|
+
it 'should support has_key?' do
|
24
|
+
$lm.has_key?("foo").should.be.true
|
25
|
+
$lm.has_key?("non-existant").should.be.false
|
26
|
+
$lm[:bar] = nil
|
27
|
+
$lm.has_key?(:bar).should.be.true
|
28
|
+
$lm.delete(:bar)
|
29
|
+
end
|
30
|
+
|
23
31
|
it 'should support the [] and []= operators' do
|
24
32
|
$lm["boo"] = "2"
|
25
33
|
$lm["boo"].should.equal "2"
|
@@ -48,12 +56,12 @@ describe 'LocalMemCache' do
|
|
48
56
|
|
49
57
|
it 'should support random_pair' do
|
50
58
|
$lm.random_pair.size.should.equal 2
|
51
|
-
LocalMemCache.drop :namespace => :empty, :force => true
|
52
|
-
ll = LocalMemCache.new :namespace => :empty
|
59
|
+
LocalMemCache.drop :namespace => :empty, :force => true, :size_mb => 2
|
60
|
+
ll = LocalMemCache.new :namespace => :empty, :size_mb => 2
|
53
61
|
ll.random_pair.should.be.nil
|
54
62
|
end
|
55
63
|
|
56
|
-
it 'should
|
64
|
+
it 'should be consistent' do
|
57
65
|
$lm.check_consistency.should.be.true
|
58
66
|
end
|
59
67
|
|
@@ -75,6 +83,12 @@ describe 'LocalMemCache' do
|
|
75
83
|
should.raise(LocalMemCache::MemoryPoolFull) { $lms["two"] = "b" * 8000000; }
|
76
84
|
end
|
77
85
|
|
86
|
+
it 'should set a minimum size' do
|
87
|
+
LocalMemCache.drop :namespace => :toosmall, :force => true
|
88
|
+
ll = LocalMemCache.new :namespace => :toosmall, :size_mb => 0.1
|
89
|
+
ll.shm_status[:total_bytes].should.equal 1024*1024
|
90
|
+
end
|
91
|
+
|
78
92
|
|
79
93
|
it 'should support clearing of the hashtable' do
|
80
94
|
($lms.size > 0).should.be.true
|
@@ -98,13 +112,10 @@ describe 'LocalMemCache' do
|
|
98
112
|
ll.size.should.equal 0
|
99
113
|
end
|
100
114
|
|
101
|
-
|
102
|
-
|
103
115
|
it 'should support checking of namespaces' do
|
104
116
|
LocalMemCache.check :namespace => "test"
|
105
117
|
end
|
106
118
|
|
107
|
-
|
108
119
|
it 'should support dropping of namespaces' do
|
109
120
|
LocalMemCache.drop :namespace => "test"
|
110
121
|
end
|
@@ -123,13 +134,14 @@ end
|
|
123
134
|
|
124
135
|
LocalMemCache.drop :namespace => "test-shared-os", :force => true
|
125
136
|
$lmsh = LocalMemCache::SharedObjectStorage.new :namespace=>"test-shared-os",
|
126
|
-
:size_mb =>
|
137
|
+
:size_mb => 2
|
127
138
|
|
128
139
|
describe 'LocalMemCache::SharedObjectStorage' do
|
129
140
|
it 'should allow to set and query for ruby objects' do
|
130
141
|
$lmsh["non-existant"].should.be.nil
|
131
|
-
$lmsh
|
142
|
+
$lmsh.set("array", [:foo, :boo])
|
132
143
|
$lmsh["array"].should.be.kind_of? Array
|
144
|
+
$lmsh.get("array").should.be.kind_of? Array
|
133
145
|
end
|
134
146
|
|
135
147
|
it 'support iteration' do
|
@@ -139,5 +151,12 @@ describe 'LocalMemCache::SharedObjectStorage' do
|
|
139
151
|
it 'support random_pair' do
|
140
152
|
$lmsh.random_pair.last.should.be.kind_of? Array
|
141
153
|
end
|
142
|
-
end
|
143
154
|
|
155
|
+
it 'should support has_key?' do
|
156
|
+
$lmsh[:foo] = 1
|
157
|
+
$lmsh.has_key?(:foo).should.be.true
|
158
|
+
$lmsh.has_key?(:non_existant).should.be.false
|
159
|
+
$lmsh[:bar] = nil
|
160
|
+
$lmsh.has_key?(:bar).should.be.true
|
161
|
+
end
|
162
|
+
end
|
data/src/tests/run-all-tests
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
$DIR=File.dirname(__FILE__)
|
2
|
+
['.', '../', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
|
3
|
+
|
4
|
+
require 'torture'
|
5
|
+
require 'localmemcache'
|
6
|
+
|
7
|
+
LocalMemCache.drop :namespace => "torture", :force => true
|
8
|
+
$h = LocalMemCache.new :namespace=>'torture', :size_mb => 200
|
9
|
+
|
10
|
+
|
11
|
+
puts "STARTED: #{$$}"
|
12
|
+
|
13
|
+
class LocalMemCache
|
14
|
+
def __set(k, v)
|
15
|
+
set(k, v)
|
16
|
+
if get(k) != v.to_s
|
17
|
+
puts "Set FAILED!"
|
18
|
+
end
|
19
|
+
raise "Consistency failed" if !check_consistency
|
20
|
+
end
|
21
|
+
def __delete(k)
|
22
|
+
delete(k)
|
23
|
+
raise "Consistency failed" if !check_consistency
|
24
|
+
end
|
25
|
+
|
26
|
+
def __clear
|
27
|
+
clear if rand * 100 > 99
|
28
|
+
end
|
29
|
+
|
30
|
+
def __each_pair
|
31
|
+
each_pair { } if rand * 100 > 99
|
32
|
+
end
|
33
|
+
|
34
|
+
def __keys
|
35
|
+
keys if rand * 100 > 99
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class TortureTesting
|
42
|
+
def self.rand_index
|
43
|
+
rand(9999)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
TortureTesting.no_progress
|
48
|
+
|
49
|
+
TortureTesting.run(2_000,
|
50
|
+
[$h, :get, [:rand_index]],
|
51
|
+
[$h, :__set, [:rand_index, :any]],
|
52
|
+
[$h, :__delete, [:rand_index]],
|
53
|
+
[$h, :__clear],
|
54
|
+
[$h, :__keys],
|
55
|
+
[$h, :random_pair],
|
56
|
+
[$h, :__each_pair]
|
57
|
+
) {|e| raise e }
|
data/src/tests/ttlmc.rb
CHANGED
@@ -7,15 +7,15 @@ require 'localmemcache'
|
|
7
7
|
LocalMemCache.drop :namespace => "torture", :force => true
|
8
8
|
$h = LocalMemCache.new :namespace=>'torture', :size_mb => 200
|
9
9
|
|
10
|
+
|
10
11
|
puts "STARTED: #{$$}"
|
11
12
|
|
12
13
|
class LocalMemCache
|
13
14
|
def __set(k, v)
|
14
15
|
set(k, v)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#end
|
16
|
+
if get(k) != v.to_s
|
17
|
+
puts "Set FAILED!"
|
18
|
+
end
|
19
19
|
end
|
20
20
|
def __delete(k)
|
21
21
|
delete(k)
|
@@ -52,4 +52,4 @@ TortureTesting.run(200_000,
|
|
52
52
|
[$h, :__keys],
|
53
53
|
[$h, :random_pair],
|
54
54
|
[$h, :__each_pair]
|
55
|
-
)
|
55
|
+
) {|e| raise e }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: localmemcache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven C. Koehler
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10
|
12
|
+
date: 2009-11-10 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -100,7 +100,6 @@ files:
|
|
100
100
|
- site/doc/index.html
|
101
101
|
- site/doc/rdoc-style.css
|
102
102
|
- site/index.html
|
103
|
-
- site/style.css
|
104
103
|
- src/Makefile.in
|
105
104
|
- src/lmc_common.c
|
106
105
|
- src/lmc_common.h
|
@@ -133,6 +132,8 @@ files:
|
|
133
132
|
- src/tests/bench_keys.rb
|
134
133
|
- src/tests/capi.c
|
135
134
|
- src/tests/capi.sh
|
135
|
+
- src/tests/console
|
136
|
+
- src/tests/console.rb
|
136
137
|
- src/tests/crash
|
137
138
|
- src/tests/crash-small
|
138
139
|
- src/tests/crash-small.rb
|
@@ -156,6 +157,8 @@ files:
|
|
156
157
|
- src/tests/ttkeys
|
157
158
|
- src/tests/ttkeys.rb
|
158
159
|
- src/tests/ttlmc
|
160
|
+
- src/tests/ttlmc-consistency
|
161
|
+
- src/tests/ttlmc-consistency.rb
|
159
162
|
- src/tests/ttlmc.rb
|
160
163
|
- src/tests/ttrandom_pair
|
161
164
|
- src/tests/ttrandom_pair.rb
|
data/site/style.css
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
body {
|
2
|
-
background: #FFF;
|
3
|
-
color: #333;
|
4
|
-
font-family: "Helvetica Neue", Helvetica, "Trebuchet MS", "Lucida Grande", Verdana, sans-serif;
|
5
|
-
line-height: 15pt;
|
6
|
-
margin: 0;
|
7
|
-
font-size: 12pt;
|
8
|
-
line-height: 1.6em;
|
9
|
-
}
|
10
|
-
|
11
|
-
h1, h2, h3, h4 {
|
12
|
-
color: #0;
|
13
|
-
padding: 0;
|
14
|
-
margin: 1em 0;
|
15
|
-
}
|
16
|
-
|
17
|
-
pre, code {
|
18
|
-
font-family: Courier;
|
19
|
-
font-size: 11pt;
|
20
|
-
overflow: auto;
|
21
|
-
}
|
22
|
-
|
23
|
-
a { color: #0088FF; text-decoration: none }
|
24
|
-
a:hover { color: #fff; background-color: #0088FF }
|
25
|
-
|
26
|
-
#content {
|
27
|
-
margin: 1em auto ;
|
28
|
-
max-width: 35em;
|
29
|
-
}
|
30
|
-
|
31
|
-
ol, ul { padding-left: 0; }
|
32
|
-
|
33
|
-
#content p {
|
34
|
-
}
|
35
|
-
#about {
|
36
|
-
list-style:none;
|
37
|
-
}
|