localmemcache 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Rakefile +1 -5
- data/THANKS +2 -1
- data/VERSION +1 -1
- data/site/index.html +42 -5
- data/src/lmc_hashtable.c +7 -4
- data/src/lmc_hashtable.h +6 -1
- data/src/lmc_lock.c +8 -2
- data/src/lmc_lock.h +1 -0
- data/src/lmc_valloc.c +17 -8
- data/src/localmemcache.c +73 -48
- data/src/localmemcache.h +9 -2
- data/src/ruby-binding/rblocalmemcache.c +20 -5
- data/src/tests/bench.rb +0 -10
- data/src/tests/capi.c +79 -0
- data/src/tests/capi.sh +6 -0
- data/src/tests/crash +1 -1
- data/src/tests/crash-small +19 -0
- data/src/tests/crash-small.rb +11 -0
- data/src/tests/crash.rb +1 -5
- data/src/tests/iter +11 -0
- data/src/tests/iter.rb +41 -0
- data/src/tests/lmc.rb +13 -0
- data/src/tests/parallelwrite +1 -1
- data/src/tests/run-all-tests +26 -0
- data/src/tests/sanity-test +7 -0
- metadata +10 -2
data/Rakefile
CHANGED
@@ -25,11 +25,7 @@ task :changelog do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
task :sanity_test do
|
28
|
-
sh "./
|
29
|
-
"(cd src/ruby-binding; ruby extconf.rb) && " +
|
30
|
-
"make -C src/ruby-binding/ && " +
|
31
|
-
"(cd src/tests; ruby extconf.rb) && " +
|
32
|
-
"make -C src/tests/ && ./src/tests/lmc "
|
28
|
+
sh "./src/tests/sanity-test"
|
33
29
|
end
|
34
30
|
|
35
31
|
task :site_rdoc do
|
data/THANKS
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
Armin Roehrl: For his thoughts and support
|
1
|
+
* Armin Roehrl: For his thoughts and support
|
2
|
+
* Paul Mineiro: Bug reports and improving iteration speed
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.3
|
data/site/index.html
CHANGED
@@ -17,7 +17,35 @@ speed</b>.
|
|
17
17
|
Since version 0.3.0 it supports <b>persistence</b>, also making it <b>a fast
|
18
18
|
alternative to GDBM, Berkeley DB, and Tokyo Cabinet</b>.
|
19
19
|
|
20
|
-
<h2>Version 0.4.
|
20
|
+
<h2>Version 0.4.3: Improving Iteration and bugfixes (2009-10-02)</h2>
|
21
|
+
|
22
|
+
Fixes by <b>Paul Mineiro</b> (thanks!):
|
23
|
+
<li><b>iteration speedup</b>: each_pair is now a lot faster when dealing with a
|
24
|
+
large dictionary. Eg: For 400k elements it's now about <b>14 times
|
25
|
+
faster</b>.</li>
|
26
|
+
<li><b>fix for leak</b> in local_memcache_free()</li>
|
27
|
+
<li><b>C API</b>: Some C API functions were not yet covered by tests. This
|
28
|
+
has been fixed.</li>
|
29
|
+
<p>
|
30
|
+
|
31
|
+
Other fixes:
|
32
|
+
<li><b>autorepair</b> fixed: Autorepair of namespaces did sometimes wrongly
|
33
|
+
report corrupt namespaces.
|
34
|
+
<li><b>:min_alloc_size</b> now also works on Ruby 1.9.</li>
|
35
|
+
|
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>
|
21
49
|
|
22
50
|
In 0.4.2 the <b>:min_alloc_size</b> parameter was introduced to help with use
|
23
51
|
cases that intend to use a hash table with growing values. This is
|
@@ -26,6 +54,7 @@ with a large list of unusable free blocks. By setting the
|
|
26
54
|
<b>:min_alloc_size</b> parameter you help the allocator to plan better
|
27
55
|
ahead. (<a href="http://localmemcache.rubyforge.org/doc/classes/LocalMemCache.html#M000001">more</a>)
|
28
56
|
|
57
|
+
</div>
|
29
58
|
|
30
59
|
<h2>Key features as of 0.4.0 (2009-05-16)</h2>
|
31
60
|
<li><a href="#performance">blazingly fast</a></li>
|
@@ -41,7 +70,7 @@ ahead. (<a href="http://localmemcache.rubyforge.org/doc/classes/LocalMemCache.ht
|
|
41
70
|
Note for <b>OS X</b>: <b>OS X</b> disqualifies as <b>HFS+</b> doesn't
|
42
71
|
have sparse files and <b>sem_timedwait</b>() and <b>sem_getvalue</b>() aren't
|
43
72
|
supported as well.<br>
|
44
|
-
Note for <b>FreeBSD</b>: It has been reported that
|
73
|
+
Note for <b>FreeBSD</b>: It has been reported that localmemcache
|
45
74
|
sometimes hangs there, it is not yet clear what the problem is.
|
46
75
|
|
47
76
|
<h2>Install</h2>
|
@@ -96,7 +125,7 @@ Ruby Hash of Strings: <b>4,963.313</b> ms
|
|
96
125
|
|
97
126
|
<p>
|
98
127
|
|
99
|
-
So, on my machine, using <b>
|
128
|
+
So, on my machine, using <b>localmemcache</b> 0.4.0 to store key-value
|
100
129
|
data on disk is about <b>10</b>% slower than keeping them in memory in a
|
101
130
|
Ruby hash of strings. It's about <b>40</b>% faster than <b>Tokyo Cabinet</b>
|
102
131
|
(which offers features similar to <b>LocalMemCache</b>).
|
@@ -109,15 +138,23 @@ can be retrieved by executing</p>
|
|
109
138
|
<pre><code>git clone git://github.com/sck/localmemcache.git
|
110
139
|
</code></pre>
|
111
140
|
|
141
|
+
<h2>Caveats</h2>
|
142
|
+
|
143
|
+
<li>Localmemcache's .lmc files are not binary compatible, they are essentially
|
144
|
+
memory mapped c structs</li>
|
145
|
+
<li>Because of the convenient auto repair feature after a lock timeout,
|
146
|
+
localmemcache is allergic to SIGSTOP (If you manage to SIGSTOP a process
|
147
|
+
while localmemcache is currently possessing a lock, that is)</li>
|
148
|
+
|
112
149
|
<h2>Who uses Localmemcache?</h2>
|
113
150
|
|
114
|
-
<a href="http://www.personifi.com">Personifi</a> use
|
151
|
+
<a href="http://www.personifi.com">Personifi</a> use localmemcache to
|
115
152
|
serve billions of hits each month. Armin Roehrl: "we use
|
116
153
|
localmemcache because it solves one problem very well and we love it!"
|
117
154
|
|
118
155
|
<h2>Tips for backups</h2>
|
119
156
|
|
120
|
-
Note that you cannot copy
|
157
|
+
Note that you cannot copy localmemcache's .lmc files while other
|
121
158
|
processes are making changes to the data, this will likely result in a
|
122
159
|
corrupt backup. So you need to make sure that none of your processes are
|
123
160
|
writing during the time you do an backup. As for copying sparse files,
|
data/src/lmc_hashtable.c
CHANGED
@@ -185,25 +185,28 @@ int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key) {
|
|
185
185
|
}
|
186
186
|
|
187
187
|
int ht_hash_iterate(void *base, va_ht_hash_t va_ht, void *ctx,
|
188
|
-
|
188
|
+
ht_iter_status_t *s, LMC_ITERATOR_P(iter)) {
|
189
189
|
va_ht_hash_entry_t va_hr;
|
190
190
|
ht_hash_entry_t *hr = &lmc_null_node;
|
191
191
|
ht_hash_t *ht = base + va_ht;
|
192
192
|
size_t k;
|
193
193
|
size_t item_c = 0;
|
194
194
|
size_t slice_counter = 0;
|
195
|
-
for (k =
|
195
|
+
for (k = s->bucket_ofs; k < LMC_HT_BUCKETS; k++) {
|
196
196
|
for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
|
197
197
|
va_hr = hr->va_next) {
|
198
198
|
hr = va_hr ? base + va_hr : 0;
|
199
|
-
if (
|
199
|
+
if (s->ofs < ++item_c) {
|
200
200
|
iter(ctx, base + hr->va_key, base + hr->va_value);
|
201
201
|
if (++slice_counter > 999) {
|
202
|
-
|
202
|
+
s->ofs = item_c;
|
203
203
|
return 2;
|
204
204
|
}
|
205
205
|
}
|
206
206
|
}
|
207
|
+
++(s->bucket_ofs);
|
208
|
+
s->ofs = 0;
|
209
|
+
item_c = 0;
|
207
210
|
}
|
208
211
|
return 1;
|
209
212
|
}
|
data/src/lmc_hashtable.h
CHANGED
@@ -16,6 +16,11 @@ typedef struct {
|
|
16
16
|
va_string_t va_value;
|
17
17
|
} ht_hash_entry_t;
|
18
18
|
|
19
|
+
typedef struct {
|
20
|
+
size_t ofs;
|
21
|
+
size_t bucket_ofs;
|
22
|
+
} ht_iter_status_t;
|
23
|
+
|
19
24
|
#define LMC_HT_BUCKETS 20731
|
20
25
|
#define LMC_ITERATOR_P(n) int ((n)) \
|
21
26
|
(void *ctx, const char *key, const char *value)
|
@@ -35,7 +40,7 @@ const char *ht_get(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key
|
|
35
40
|
size_t *n_value);
|
36
41
|
int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key);
|
37
42
|
int ht_hash_destroy(void *base, va_ht_hash_t ht);
|
38
|
-
int ht_hash_iterate(void *base, va_ht_hash_t ht, void *ctx,
|
43
|
+
int ht_hash_iterate(void *base, va_ht_hash_t ht, void *ctx, ht_iter_status_t *s,
|
39
44
|
LMC_ITERATOR_P(iter));
|
40
45
|
int ht_random_pair(void *base, va_ht_hash_t va_ht, char **r_key,
|
41
46
|
size_t *n_key, char **r_value, size_t *n_value);
|
data/src/lmc_lock.c
CHANGED
@@ -32,13 +32,19 @@ lmc_lock_t *lmc_lock_init(const char *ns, int init, lmc_error_t *e) {
|
|
32
32
|
return l;
|
33
33
|
}
|
34
34
|
|
35
|
+
void lmc_lock_free(lmc_lock_t* l) {
|
36
|
+
if (!l) return;
|
37
|
+
if (l->sem) { sem_close(l->sem); }
|
38
|
+
free(l);
|
39
|
+
}
|
40
|
+
|
35
41
|
int lmc_clear_namespace_lock(const char *ns) {
|
36
42
|
char namespace[1024];
|
37
43
|
lmc_namespacify(namespace, ns);
|
38
44
|
lmc_error_t e;
|
39
45
|
lmc_lock_t *l = lmc_lock_init(namespace, 1, &e);
|
40
46
|
lmc_lock_repair(l);
|
41
|
-
|
47
|
+
lmc_lock_free(l);
|
42
48
|
return 1;
|
43
49
|
}
|
44
50
|
|
@@ -72,7 +78,7 @@ int lmc_sem_timed_wait_mandatory(lmc_lock_t* l) {
|
|
72
78
|
#else
|
73
79
|
struct timespec ts;
|
74
80
|
clock_gettime(CLOCK_REALTIME, &ts);
|
75
|
-
ts.tv_sec +=
|
81
|
+
ts.tv_sec += 60;
|
76
82
|
return sem_timedwait(l->sem, &ts);
|
77
83
|
#endif
|
78
84
|
}
|
data/src/lmc_lock.h
CHANGED
@@ -13,6 +13,7 @@ typedef struct {
|
|
13
13
|
} lmc_lock_t;
|
14
14
|
|
15
15
|
lmc_lock_t *lmc_lock_init(const char *namespace, int init, lmc_error_t *e);
|
16
|
+
void lmc_lock_free(lmc_lock_t* l);
|
16
17
|
int lmc_lock_obtain(const char *where, lmc_lock_t* l, lmc_error_t *e);
|
17
18
|
int lmc_lock_obtain_mandatory(const char *where, lmc_lock_t* l, lmc_error_t *e);
|
18
19
|
int lmc_lock_release(const char *where, lmc_lock_t* l, lmc_error_t *e);
|
data/src/lmc_valloc.c
CHANGED
@@ -360,11 +360,11 @@ void lmc_free(void *base, size_t chunk) {
|
|
360
360
|
__lmc_free(base, va_used_chunk, uc_size);
|
361
361
|
}
|
362
362
|
|
363
|
-
int lmc_um_getbit(char *bf,
|
363
|
+
int lmc_um_getbit(char *bf, size_t i) {
|
364
364
|
bf += i / 8; return (*bf & (1 << (i % 8))) != 0;
|
365
365
|
}
|
366
366
|
|
367
|
-
void lmc_um_setbit(char *bf,
|
367
|
+
void lmc_um_setbit(char *bf, size_t i, size_t v) {
|
368
368
|
bf += i / 8;
|
369
369
|
if (v) *bf |= 1 << (i % 8);
|
370
370
|
else *bf &= ~(1 << (i % 8));
|
@@ -382,7 +382,8 @@ int lmc_um_find_leaks(void *base, char *bf) {
|
|
382
382
|
for (i = 0; i < md->total_size; ++i) {
|
383
383
|
if (!gap) {
|
384
384
|
size_t *b = (void *)bf + i / 8;
|
385
|
-
|
385
|
+
size_t ee = md->total_size - sizeof(size_t) * 8;
|
386
|
+
while (*b == m && i < ee) {
|
386
387
|
i += sizeof(size_t) * 8; b++;
|
387
388
|
}
|
388
389
|
}
|
@@ -415,10 +416,13 @@ int lmc_um_check_unmarked(void *base, char *bf, size_t va, size_t size) {
|
|
415
416
|
size_t end = va + size;
|
416
417
|
for (i = va; i < va + size; ++i) {
|
417
418
|
size_t *b = (void *)bf + i / 8;
|
418
|
-
|
419
|
+
size_t ee = end - sizeof(size_t) * 8;
|
420
|
+
while (*b == n && i < ee) {
|
419
421
|
i += sizeof(size_t) * 8; b++;
|
420
422
|
}
|
421
|
-
if (lmc_um_getbit(bf, i) != 0) {
|
423
|
+
if (lmc_um_getbit(bf, i) != 0) {
|
424
|
+
return 0;
|
425
|
+
}
|
422
426
|
}
|
423
427
|
return 1;
|
424
428
|
}
|
@@ -428,11 +432,15 @@ int lmc_um_mark(void *base, char *bf, size_t va, size_t size) {
|
|
428
432
|
lmc_mem_descriptor_t *md = base;
|
429
433
|
if ((va > sizeof(lmc_mem_descriptor_t)) &&
|
430
434
|
(!lmc_is_va_valid(base, va) || !lmc_is_va_valid(base, va + size))) {
|
431
|
-
|
435
|
+
fprintf(stderr, "[localmemcache] Error: VA start out of range: "
|
432
436
|
"va: %zd - %zd max %zd!\n", va, va + size, md->total_size);
|
433
437
|
return 0;
|
434
438
|
}
|
435
|
-
if (!lmc_um_check_unmarked(base, bf, va, size))
|
439
|
+
if (!lmc_um_check_unmarked(base, bf, va, size)) {
|
440
|
+
fprintf(stderr, "[localmemcache] Error: Part of a block to be "
|
441
|
+
"marked used is used already (va: %zd s: %zd) !\n", va, size);
|
442
|
+
return 0;
|
443
|
+
}
|
436
444
|
for (i = va; i < va + size; ++i) {
|
437
445
|
if (i % 8 == 0) {
|
438
446
|
size_t b_start = i / 8;
|
@@ -450,7 +458,8 @@ int lmc_um_mark(void *base, char *bf, size_t va, size_t size) {
|
|
450
458
|
int lmc_um_mark_allocated(void *base, char *bf, size_t va) {
|
451
459
|
size_t real_va = va - sizeof(size_t);
|
452
460
|
size_t s = *(size_t *)(base + real_va);
|
453
|
-
|
461
|
+
int r = lmc_um_mark(base, bf, real_va, s);
|
462
|
+
return r;
|
454
463
|
}
|
455
464
|
|
456
465
|
char *lmc_um_new_mem_usage_bitmap(void *base) {
|
data/src/localmemcache.c
CHANGED
@@ -56,6 +56,8 @@ int lmc_namespace_or_filename(char *result, const char* ons, const char *ofn,
|
|
56
56
|
return 0;
|
57
57
|
}
|
58
58
|
|
59
|
+
void lmc_checkize(char *result, char *s) { snprintf(result, 1023, "%s-check", s); }
|
60
|
+
|
59
61
|
int local_memcache_drop_namespace(const char *namespace, const char *filename,
|
60
62
|
int force, lmc_error_t *e) {
|
61
63
|
char clean_ns[1024];
|
@@ -65,13 +67,13 @@ int local_memcache_drop_namespace(const char *namespace, const char *filename,
|
|
65
67
|
if (force) {
|
66
68
|
lmc_lock_t *l = lmc_lock_init((char *)clean_ns, 1, e);
|
67
69
|
lmc_lock_repair(l);
|
68
|
-
|
70
|
+
lmc_lock_free(l);
|
69
71
|
char check_lock_name[1024];
|
70
|
-
|
72
|
+
lmc_checkize(check_lock_name, clean_ns);
|
71
73
|
lmc_lock_t *check_l;
|
72
74
|
check_l = lmc_lock_init(check_lock_name, 1, e);
|
73
75
|
lmc_lock_repair(check_l);
|
74
|
-
|
76
|
+
lmc_lock_free(check_l);
|
75
77
|
}
|
76
78
|
return 1;
|
77
79
|
}
|
@@ -159,9 +161,45 @@ local_memcache_t *local_memcache_create(const char *namespace,
|
|
159
161
|
return __local_memcache_create((char *)clean_ns, si, min_alloc_size, 0, 0, e);
|
160
162
|
}
|
161
163
|
|
164
|
+
int lmc_lock_shm_region(const char *who, local_memcache_t *lmc) {
|
165
|
+
int r;
|
166
|
+
int retry_counter = 0;
|
167
|
+
retry:
|
168
|
+
if (retry_counter++ > 10) {
|
169
|
+
fprintf(stderr, "[localmemcache] Too many retries: "
|
170
|
+
"Cannot repair namespace '%s'\n", lmc->namespace);
|
171
|
+
return 0;
|
172
|
+
}
|
173
|
+
r = lmc_lock_obtain(who, lmc->lock, &lmc->error);
|
174
|
+
if (!r && (strcmp(lmc->error.error_type, "LockTimedOut") == 0)) {
|
175
|
+
if (__local_memcache_check_namespace(lmc->namespace,
|
176
|
+
&lmc->error)) goto retry;
|
177
|
+
fprintf(stderr, "[localmemcache] Cannot repair namespace '%s'\n",
|
178
|
+
lmc->namespace);
|
179
|
+
}
|
180
|
+
if (!r) return 0;
|
181
|
+
if (!lmc_set_lock_flag(lmc->base, &lmc->error)) {
|
182
|
+
lmc_lock_release(who, lmc->lock, &lmc->error);
|
183
|
+
return 0;
|
184
|
+
}
|
185
|
+
return 1;
|
186
|
+
}
|
187
|
+
|
188
|
+
int __local_memcache_free(local_memcache_t *lmc, lmc_error_t *e, int lock) {
|
189
|
+
if (lock && !lmc_lock_shm_region("local_memcache_free", lmc)) return 0;
|
190
|
+
int r = ht_hash_destroy(lmc->base, lmc->va_hash);
|
191
|
+
if (lock && !lmc_unlock_shm_region("local_memcache_free", lmc)) return 0;
|
192
|
+
lmc_shm_destroy(lmc->shm, e);
|
193
|
+
free(lmc->namespace);
|
194
|
+
lmc_lock_free(lmc->lock);
|
195
|
+
free(lmc);
|
196
|
+
return r;
|
197
|
+
}
|
198
|
+
|
162
199
|
int __local_memcache_check_namespace(const char *clean_ns, lmc_error_t *e) {
|
200
|
+
int repair_failed = 0;
|
163
201
|
char check_lock_name[1024];
|
164
|
-
|
202
|
+
lmc_checkize(check_lock_name, (char *)clean_ns);
|
165
203
|
|
166
204
|
if (!lmc_does_namespace_exist((char *)clean_ns)) {
|
167
205
|
lmc_clear_namespace_lock(check_lock_name);
|
@@ -216,17 +254,21 @@ release:
|
|
216
254
|
release_but_no_lock_correction:
|
217
255
|
local_memcache_free(lmc, e);
|
218
256
|
lmc_lock_release("local_memcache_check_namespace", check_l, e);
|
219
|
-
|
257
|
+
lmc_lock_free(check_l);
|
220
258
|
return 1;
|
221
259
|
failed:
|
260
|
+
repair_failed = 1;
|
222
261
|
lmc_handle_error_with_err_string("local_memcache_check_namespace",
|
223
262
|
"Unable to recover namespace", "RecoveryFailed", e);
|
224
|
-
|
263
|
+
__local_memcache_free(lmc, e, 0);
|
225
264
|
lmc_lock_release("local_memcache_check_namespace", check_l, e);
|
265
|
+
fprintf(stderr, "[localmemcache] Recovery failed!\n");
|
226
266
|
check_lock_failed:
|
227
|
-
|
228
|
-
|
229
|
-
|
267
|
+
lmc_lock_free(check_l);
|
268
|
+
if (!repair_failed) {
|
269
|
+
fprintf(stderr, "[localmemcache] Failed to obtain the 'check lock' to repair "
|
270
|
+
"namespace '%s'\n", clean_ns);
|
271
|
+
}
|
230
272
|
return 0;
|
231
273
|
}
|
232
274
|
|
@@ -238,30 +280,6 @@ int local_memcache_check_namespace(const char *namespace, const char *filename,
|
|
238
280
|
return __local_memcache_check_namespace(clean_ns, e);
|
239
281
|
}
|
240
282
|
|
241
|
-
int lmc_lock_shm_region(const char *who, local_memcache_t *lmc) {
|
242
|
-
int r;
|
243
|
-
int retry_counter = 0;
|
244
|
-
retry:
|
245
|
-
if (retry_counter++ > 10) {
|
246
|
-
fprintf(stderr, "[localmemcache] Too many retries: "
|
247
|
-
"Cannot repair namespace '%s'\n", lmc->namespace);
|
248
|
-
return 0;
|
249
|
-
}
|
250
|
-
r = lmc_lock_obtain(who, lmc->lock, &lmc->error);
|
251
|
-
if (!r && (strcmp(lmc->error.error_type, "LockTimedOut") == 0)) {
|
252
|
-
if (__local_memcache_check_namespace(lmc->namespace,
|
253
|
-
&lmc->error)) goto retry;
|
254
|
-
fprintf(stderr, "[localmemcache] Cannot repair namespace '%s'\n",
|
255
|
-
lmc->namespace);
|
256
|
-
}
|
257
|
-
if (!r) return 0;
|
258
|
-
if (!lmc_set_lock_flag(lmc->base, &lmc->error)) {
|
259
|
-
lmc_lock_release(who, lmc->lock, &lmc->error);
|
260
|
-
return 0;
|
261
|
-
}
|
262
|
-
return 1;
|
263
|
-
}
|
264
|
-
|
265
283
|
int lmc_unlock_shm_region(const char *who, local_memcache_t *lmc) {
|
266
284
|
int r = 1;
|
267
285
|
if (!lmc_release_lock_flag(lmc->base, &lmc->error)) r = 0;
|
@@ -279,8 +297,11 @@ const char *__local_memcache_get(local_memcache_t *lmc,
|
|
279
297
|
char *local_memcache_get_new(local_memcache_t *lmc,
|
280
298
|
const char *key, size_t n_key, size_t *n_value) {
|
281
299
|
const char *r = __local_memcache_get(lmc, key, n_key, n_value);
|
282
|
-
char *new_s =
|
283
|
-
|
300
|
+
char *new_s = 0;
|
301
|
+
if (r) {
|
302
|
+
new_s = malloc(*n_value);
|
303
|
+
memcpy(new_s, r, *n_value);
|
304
|
+
}
|
284
305
|
if (!lmc_unlock_shm_region("local_memcache_get_new", lmc)) return 0;
|
285
306
|
return new_s;
|
286
307
|
}
|
@@ -296,11 +317,13 @@ int local_memcache_random_pair_new(local_memcache_t *lmc,
|
|
296
317
|
char **r_key, size_t *n_key, char **r_value, size_t *n_value) {
|
297
318
|
char *k;
|
298
319
|
char *v;
|
320
|
+
(*r_key) = 0;
|
321
|
+
(*r_value) = 0;
|
299
322
|
if (__local_memcache_random_pair(lmc, &k, n_key, &v, n_value)) {
|
300
|
-
|
301
|
-
memcpy(
|
302
|
-
|
303
|
-
memcpy(
|
323
|
+
(*r_key) = malloc(*n_key);
|
324
|
+
memcpy((*r_key), k, *n_key);
|
325
|
+
(*r_value) = malloc(*n_value);
|
326
|
+
memcpy((*r_value), v, *n_value);
|
304
327
|
}
|
305
328
|
if (!lmc_unlock_shm_region("local_memcache_random_pair", lmc)) return 0;
|
306
329
|
return 1;
|
@@ -334,19 +357,21 @@ int local_memcache_delete(local_memcache_t *lmc, char *key, size_t n_key) {
|
|
334
357
|
}
|
335
358
|
|
336
359
|
int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e) {
|
337
|
-
|
338
|
-
int r = ht_hash_destroy(lmc->base, lmc->va_hash);
|
339
|
-
if (!lmc_unlock_shm_region("local_memcache_free", lmc)) return 0;
|
340
|
-
lmc_shm_destroy(lmc->shm, e);
|
341
|
-
free(lmc->namespace);
|
342
|
-
free(lmc->lock);
|
343
|
-
return r;
|
360
|
+
return __local_memcache_free(lmc, e, 1);
|
344
361
|
}
|
345
362
|
|
346
363
|
int local_memcache_iterate(local_memcache_t *lmc, void *ctx,
|
347
|
-
|
364
|
+
ht_iter_status_t *s, LMC_ITERATOR_P(iter)) {
|
348
365
|
if (!lmc_lock_shm_region("local_memcache_iterate", lmc)) return 0;
|
349
|
-
int r = ht_hash_iterate(lmc->base, lmc->va_hash, ctx,
|
366
|
+
int r = ht_hash_iterate(lmc->base, lmc->va_hash, ctx, s, iter);
|
350
367
|
if (!lmc_unlock_shm_region("local_memcache_iterate", lmc)) return 0;
|
351
368
|
return r;
|
352
369
|
}
|
370
|
+
|
371
|
+
int local_memcache_check_consistency(local_memcache_t *lmc, lmc_error_t *e) {
|
372
|
+
lmc_mem_descriptor_t *md = lmc->base;
|
373
|
+
if (!lmc_lock_shm_region("local_memcache_check_consistency", lmc)) return 0;
|
374
|
+
int r = ht_check_memory(lmc->base, md->va_hash);
|
375
|
+
if (!lmc_unlock_shm_region("local_memcache_check_consistency", lmc)) return 0;
|
376
|
+
return r;
|
377
|
+
}
|
data/src/localmemcache.h
CHANGED
@@ -170,7 +170,9 @@ int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e);
|
|
170
170
|
*
|
171
171
|
* local_memcache_t *lmc;
|
172
172
|
* collector_t c;
|
173
|
-
*
|
173
|
+
* ht_iter_status_t s;
|
174
|
+
* memset(s, sizeof(ht_iter_status_t));
|
175
|
+
* local_memcache_iterate(lmc, (void *) &c, &s, my_collect);
|
174
176
|
*
|
175
177
|
* The memory pool will be locked while iteration takes place, so try to make
|
176
178
|
* sure you can iterate within under 2 seconds otherwise other waiting
|
@@ -178,7 +180,7 @@ int local_memcache_free(local_memcache_t *lmc, lmc_error_t *e);
|
|
178
180
|
* triggering the automatic recovery.)
|
179
181
|
*
|
180
182
|
*/
|
181
|
-
int local_memcache_iterate(local_memcache_t *lmc, void *ctx,
|
183
|
+
int local_memcache_iterate(local_memcache_t *lmc, void *ctx, ht_iter_status_t *s,
|
182
184
|
LMC_ITERATOR_P(iter));
|
183
185
|
|
184
186
|
/*
|
@@ -218,6 +220,7 @@ int local_memcache_drop_namespace(const char *namespace, const char *filename,
|
|
218
220
|
int local_memcache_check_namespace(const char *namespace, const char *filename,
|
219
221
|
lmc_error_t *e);
|
220
222
|
|
223
|
+
|
221
224
|
/* internal, do not use */
|
222
225
|
const char *__local_memcache_get(local_memcache_t *lmc,
|
223
226
|
const char *key, size_t n_key, size_t *n_value);
|
@@ -228,4 +231,8 @@ int __local_memcache_random_pair(local_memcache_t *lmc,
|
|
228
231
|
|
229
232
|
/* internal, do not use */
|
230
233
|
int lmc_unlock_shm_region(const char *who, local_memcache_t *lmc);
|
234
|
+
|
235
|
+
/* internal, do not use */
|
236
|
+
int local_memcache_check_consistency(local_memcache_t *lmc, lmc_error_t *e);
|
237
|
+
|
231
238
|
#endif
|
@@ -29,7 +29,7 @@
|
|
29
29
|
#endif
|
30
30
|
|
31
31
|
/* :nodoc: */
|
32
|
-
long long_value(VALUE i) { return NUM2LONG(rb_Integer(i)); }
|
32
|
+
long long_value(VALUE i) { return NIL_P(i) ? 0 : NUM2LONG(rb_Integer(i)); }
|
33
33
|
/* :nodoc: */
|
34
34
|
double double_value(VALUE i) { return NUM2DBL(i); }
|
35
35
|
/* :nodoc: */
|
@@ -322,10 +322,11 @@ static VALUE __LocalMemCache__keys(VALUE d) {
|
|
322
322
|
lmc_ruby_iter_collect_keys data;
|
323
323
|
data.ary = r;
|
324
324
|
int success = 2;
|
325
|
-
|
325
|
+
ht_iter_status_t s;
|
326
|
+
memset(&s, 0x0, sizeof(ht_iter_status_t));
|
326
327
|
while (success == 2) {
|
327
328
|
success = local_memcache_iterate(get_LocalMemCache(obj),
|
328
|
-
(void *) &data, &
|
329
|
+
(void *) &data, &s, lmc_ruby_iter);
|
329
330
|
}
|
330
331
|
if (!success) { return Qnil; }
|
331
332
|
return Qnil;
|
@@ -366,13 +367,14 @@ int lmc_ruby_iter_collect_pairs(void *ctx, const char* key, const char* value) {
|
|
366
367
|
static VALUE __LocalMemCache__each_pair(VALUE d) {
|
367
368
|
VALUE obj = rb_ary_entry(d, 0);
|
368
369
|
int success = 2;
|
369
|
-
|
370
|
+
ht_iter_status_t s;
|
371
|
+
memset(&s, 0x0, sizeof(ht_iter_status_t));
|
370
372
|
while (success == 2) {
|
371
373
|
VALUE r = rb_ary_new();
|
372
374
|
lmc_ruby_iter_collect_pairs_t data;
|
373
375
|
data.ary = r;
|
374
376
|
success = local_memcache_iterate(get_LocalMemCache(obj),
|
375
|
-
(void *) &data, &
|
377
|
+
(void *) &data, &s, lmc_ruby_iter_collect_pairs);
|
376
378
|
long i;
|
377
379
|
for (i = 0; i < RARRAY_LEN(r); i++) {
|
378
380
|
rb_yield(RARRAY_PTR(r)[i]);
|
@@ -412,6 +414,17 @@ static VALUE LocalMemCache__size(VALUE obj) {
|
|
412
414
|
return rb_int2big(ht->size);
|
413
415
|
}
|
414
416
|
|
417
|
+
/*
|
418
|
+
* internal, do not use
|
419
|
+
*/
|
420
|
+
static VALUE LocalMemCache__check_consistency(VALUE obj) {
|
421
|
+
lmc_error_t e;
|
422
|
+
rb_lmc_handle_t *h;
|
423
|
+
Data_Get_Struct(obj, rb_lmc_handle_t, h);
|
424
|
+
return local_memcache_check_consistency(rb_lmc_check_handle_access(h), &e) ?
|
425
|
+
Qtrue : Qfalse;
|
426
|
+
}
|
427
|
+
|
415
428
|
/*
|
416
429
|
* Document-class: LocalMemCache
|
417
430
|
*
|
@@ -487,6 +500,8 @@ void Init_rblocalmemcache() {
|
|
487
500
|
0);
|
488
501
|
rb_define_method(LocalMemCache, "close", LocalMemCache__close, 0);
|
489
502
|
rb_define_method(LocalMemCache, "size", LocalMemCache__size, 0);
|
503
|
+
rb_define_method(LocalMemCache, "check_consistency",
|
504
|
+
LocalMemCache__check_consistency, 0);
|
490
505
|
|
491
506
|
lmc_rb_sym_namespace = ID2SYM(rb_intern("namespace"));
|
492
507
|
lmc_rb_sym_filename = ID2SYM(rb_intern("filename"));
|
data/src/tests/bench.rb
CHANGED
@@ -49,13 +49,3 @@ def measure_time(c, &block)
|
|
49
49
|
end
|
50
50
|
|
51
51
|
compare_speed(2_000_000)
|
52
|
-
#test_gdbm(2_000_000)
|
53
|
-
|
54
|
-
#$stdout.write "ht shm setting x 20000: "
|
55
|
-
#tmeasure (2_000_000) {
|
56
|
-
# v = $lm2.get("f").to_i + 1
|
57
|
-
# #puts "v:#{v}"
|
58
|
-
# $lm2.set("f", v)
|
59
|
-
#}
|
60
|
-
#puts "foo: #{$lm2.get("f")}"
|
61
|
-
|
data/src/tests/capi.c
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <localmemcache.h>
|
3
|
+
|
4
|
+
#define fail_if(t,m) \
|
5
|
+
if ((t)) { printf("FAILED: %s\n", (m)); goto test_failed; } \
|
6
|
+
else { printf(" - %s\n", (m)); }
|
7
|
+
|
8
|
+
#define TEST_START(m) \
|
9
|
+
printf("%s\n", (m)); \
|
10
|
+
{
|
11
|
+
|
12
|
+
#define TEST_END \
|
13
|
+
}
|
14
|
+
|
15
|
+
int main() {
|
16
|
+
lmc_error_t e;
|
17
|
+
if (!local_memcache_drop_namespace("capitest", 0, 1, &e)) {
|
18
|
+
fprintf(stderr, "drop failed: %s\n", e.error_str);
|
19
|
+
return 1;
|
20
|
+
}
|
21
|
+
local_memcache_t *lmc = local_memcache_create("capitest", 0, 0, 0, &e);
|
22
|
+
if (!lmc) {
|
23
|
+
fprintf(stderr, "Couldn't create localmemcache: %s\n", e.error_str);
|
24
|
+
return 1;
|
25
|
+
}
|
26
|
+
if (!local_memcache_set(lmc, "foo", 3, "1", 1)) goto failed;
|
27
|
+
|
28
|
+
TEST_START("local_memcache_get_new")
|
29
|
+
size_t n_value;
|
30
|
+
char *value = local_memcache_get_new(lmc, "foo", 3, &n_value);
|
31
|
+
fail_if(!value, "value")
|
32
|
+
fail_if(n_value != 1, "n_value");
|
33
|
+
fail_if(value[0] != '1', "value[0]");
|
34
|
+
free(value);
|
35
|
+
|
36
|
+
char *value2 = local_memcache_get_new(lmc, "unknown", 7, &n_value);
|
37
|
+
fail_if(value2, "value2");
|
38
|
+
TEST_END
|
39
|
+
|
40
|
+
TEST_START("random_pair")
|
41
|
+
char *rk, *rv;
|
42
|
+
size_t n_rk, n_rv;
|
43
|
+
int r = local_memcache_random_pair_new(lmc, &rk, &n_rk, &rv, &n_rv);
|
44
|
+
fail_if(!r, "local_memcache_random_pair_new");
|
45
|
+
fail_if(n_rk != 3, "n_rk");
|
46
|
+
fail_if(rk[0] != 'f', "rk0");
|
47
|
+
fail_if(n_rv != 1, "n_rv");
|
48
|
+
fail_if(rv[0] != '1', "rv0");
|
49
|
+
free(rk);
|
50
|
+
free(rv);
|
51
|
+
TEST_END
|
52
|
+
|
53
|
+
if (!local_memcache_delete(lmc, "foo", 3)) goto failed;
|
54
|
+
|
55
|
+
TEST_START("random_pair(empty)")
|
56
|
+
char *rk, *rv;
|
57
|
+
size_t n_rk, n_rv;
|
58
|
+
int r = local_memcache_random_pair_new(lmc, &rk, &n_rk, &rv, &n_rv);
|
59
|
+
fail_if(!r, "local_memcache_random_pair_new:empty");
|
60
|
+
fail_if(rk, "rk:empty")
|
61
|
+
fail_if(rv, "rv:empty")
|
62
|
+
TEST_END
|
63
|
+
|
64
|
+
printf("Ok\n");
|
65
|
+
|
66
|
+
test_failed:
|
67
|
+
if (!local_memcache_free(lmc, &e)) {
|
68
|
+
fprintf(stderr, "Failed to release localmemcache: %s\n", e.error_str);
|
69
|
+
return 1;
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
return 0;
|
74
|
+
|
75
|
+
failed:
|
76
|
+
fprintf(stderr, "FAILED: %s\n", lmc->error.error_str);
|
77
|
+
return 1;
|
78
|
+
|
79
|
+
}
|
data/src/tests/capi.sh
ADDED
data/src/tests/crash
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
D=`dirname $0`
|
3
|
+
DIR=`cd $D; pwd`
|
4
|
+
script=$DIR/crash-small.rb
|
5
|
+
|
6
|
+
export CFLAGS=-DDO_TEST_CRASH
|
7
|
+
make -C .. clean && make -C .. && make -C ../ruby-binding &&
|
8
|
+
ruby extconf.rb && make clean && make || exit 1
|
9
|
+
|
10
|
+
#ulimit -c 0
|
11
|
+
|
12
|
+
if test "x$1" = "x-d"; then
|
13
|
+
irb -r $script
|
14
|
+
else
|
15
|
+
#valgrind --leak-check=full --tool=memcheck ruby $script
|
16
|
+
ruby $script
|
17
|
+
fi
|
18
|
+
|
19
|
+
#ulimit -c unlimited
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$DIR=File.dirname(__FILE__)
|
2
|
+
['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
|
3
|
+
|
4
|
+
require 'localmemcache'
|
5
|
+
|
6
|
+
LocalMemCache.drop :namespace => "crash-small-t", :force => true
|
7
|
+
$lm = LocalMemCache.new :namespace=>"crash-small-t"
|
8
|
+
|
9
|
+
$lm["one"] = "1"
|
10
|
+
$lm.check_consistency
|
11
|
+
|
data/src/tests/crash.rb
CHANGED
@@ -4,10 +4,6 @@ $DIR=File.dirname(__FILE__)
|
|
4
4
|
require 'localmemcache'
|
5
5
|
|
6
6
|
LocalMemCache.drop :namespace => "crash-t", :force => true
|
7
|
-
#exit
|
8
|
-
#puts "c"
|
9
|
-
#LocalMemCache.check_namespace("crash-t");
|
10
|
-
|
11
7
|
|
12
8
|
$pids = []
|
13
9
|
5.times { $pids << fork {
|
@@ -23,7 +19,7 @@ $pids = []
|
|
23
19
|
puts "#{$$} Worker finished"
|
24
20
|
}}
|
25
21
|
|
26
|
-
|
22
|
+
10.times {
|
27
23
|
$pid = fork {
|
28
24
|
LocalMemCache.enable_test_crash
|
29
25
|
$lm2 = LocalMemCache.new :namespace=>"crash-t"
|
data/src/tests/iter
ADDED
data/src/tests/iter.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
$DIR=File.dirname(__FILE__)
|
2
|
+
['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
|
3
|
+
|
4
|
+
require 'bacon'
|
5
|
+
require 'localmemcache'
|
6
|
+
|
7
|
+
Bacon.summary_on_exit
|
8
|
+
|
9
|
+
LocalMemCache.drop :namespace => "iteration", :force => true
|
10
|
+
$l = LocalMemCache.new :namespace=>"iteration"
|
11
|
+
|
12
|
+
def compare_speed(n)
|
13
|
+
|
14
|
+
puts "lmc: filling dict"
|
15
|
+
measure_time(1) { n.times {|i| $l[i] = i } }
|
16
|
+
puts "lmc: iterating"
|
17
|
+
c = 0
|
18
|
+
measure_time(1) {
|
19
|
+
$l.each_pair {|k,v| c += 1 }
|
20
|
+
}
|
21
|
+
raise "lmc: error: c(#{c})!= n(#{n})" if c != n
|
22
|
+
|
23
|
+
puts "rhash: filling dict"
|
24
|
+
$hh = {}
|
25
|
+
measure_time(1) { n.times {|i| $hh[i] = i } }
|
26
|
+
puts "rhash: iterating"
|
27
|
+
c = 0
|
28
|
+
measure_time(1) {
|
29
|
+
$hh.each {|k,v| c += 1 }
|
30
|
+
}
|
31
|
+
raise "rhash: error: c!= n" if c != n
|
32
|
+
end
|
33
|
+
|
34
|
+
def measure_time(c, &block)
|
35
|
+
_then = Time.now
|
36
|
+
c.times { block.call }
|
37
|
+
now = Time.now
|
38
|
+
puts "#{(now - _then)*1000} ms"
|
39
|
+
end
|
40
|
+
|
41
|
+
compare_speed(400_000)
|
data/src/tests/lmc.rb
CHANGED
@@ -53,6 +53,18 @@ describe 'LocalMemCache' do
|
|
53
53
|
ll.random_pair.should.be.nil
|
54
54
|
end
|
55
55
|
|
56
|
+
it 'should allow be consistent' do
|
57
|
+
$lm.check_consistency.should.be.true
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should support iteration' do
|
61
|
+
s = $lm.size
|
62
|
+
s.should.equal 3
|
63
|
+
c = 0
|
64
|
+
$lm.each_pair {|a,b| c += 1 }
|
65
|
+
s.should.equal c
|
66
|
+
end
|
67
|
+
|
56
68
|
it 'should throw an exception when accessing a closed pool' do
|
57
69
|
$lm.close
|
58
70
|
should.raise(LocalMemCache::MemoryPoolClosed) { $lm.keys }
|
@@ -87,6 +99,7 @@ describe 'LocalMemCache' do
|
|
87
99
|
end
|
88
100
|
|
89
101
|
|
102
|
+
|
90
103
|
it 'should support checking of namespaces' do
|
91
104
|
LocalMemCache.check :namespace => "test"
|
92
105
|
end
|
data/src/tests/parallelwrite
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
|
4
|
+
echo "SHM"
|
5
|
+
./shm
|
6
|
+
echo "LMC"
|
7
|
+
./lmc
|
8
|
+
echo "capi"
|
9
|
+
./capi.sh
|
10
|
+
|
11
|
+
echo "bench"
|
12
|
+
./bench
|
13
|
+
echo "bench-append"
|
14
|
+
./bench-append
|
15
|
+
|
16
|
+
echo "ttlmc"
|
17
|
+
./ttlmc
|
18
|
+
echo "ttrandom_pair"
|
19
|
+
./ttrandom_pair
|
20
|
+
echo "ttalloc"
|
21
|
+
./ttalloc
|
22
|
+
|
23
|
+
echo "parallelwrite"
|
24
|
+
./parallelwrite
|
25
|
+
echo "crash"
|
26
|
+
./crash
|
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.3
|
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-
|
12
|
+
date: 2009-10-02 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -131,15 +131,23 @@ files:
|
|
131
131
|
- src/tests/bench.rb
|
132
132
|
- src/tests/bench_keys
|
133
133
|
- src/tests/bench_keys.rb
|
134
|
+
- src/tests/capi.c
|
135
|
+
- src/tests/capi.sh
|
134
136
|
- src/tests/crash
|
137
|
+
- src/tests/crash-small
|
138
|
+
- src/tests/crash-small.rb
|
135
139
|
- src/tests/crash.rb
|
136
140
|
- src/tests/extconf.rb
|
141
|
+
- src/tests/iter
|
142
|
+
- src/tests/iter.rb
|
137
143
|
- src/tests/lmc
|
138
144
|
- src/tests/lmc.rb
|
139
145
|
- src/tests/lmctestapi.c
|
140
146
|
- src/tests/parallelwrite
|
141
147
|
- src/tests/parallelwrite.rb
|
148
|
+
- src/tests/run-all-tests
|
142
149
|
- src/tests/runtest.sh
|
150
|
+
- src/tests/sanity-test
|
143
151
|
- src/tests/shm
|
144
152
|
- src/tests/shm.rb
|
145
153
|
- src/tests/torture.rb
|