localmemcache 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|