fsdb 0.7.1 → 0.7.2
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/History.txt +12 -0
- data/README.markdown +7 -23
- data/examples/data/hello.png +0 -0
- data/examples/fixture.rb +45 -0
- data/lib/fsdb/database.rb +24 -17
- data/lib/fsdb/file-lock.rb +4 -4
- data/lib/fsdb/modex.rb +2 -0
- data/lib/fsdb/persistent.rb +2 -2
- metadata +6 -3
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
fsdb 0.7.2
|
2
|
+
|
3
|
+
- rename lock_shared and lock_exclusive by appending _fsdb to avoid conflicts
|
4
|
+
- require thread for 1.8.7 compat
|
5
|
+
- added fixture example
|
6
|
+
- fetch clears cache entry to prevent increasing memory use
|
7
|
+
- fix possible race condition in insert
|
8
|
+
- use db cache_mutex instead of Thread.exclusive.
|
9
|
+
- minor api doc changes
|
10
|
+
- stop using literal character notation, for clarity
|
11
|
+
- updated README with recent benchmarks
|
12
|
+
|
1
13
|
fsdb 0.7.1
|
2
14
|
|
3
15
|
- added a require needed for ruby 1.8
|
data/README.markdown
CHANGED
@@ -291,17 +291,17 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
|
|
291
291
|
or FSDB dump/load methods that use a faster format (e.g., plain text, rather
|
292
292
|
than a marshalled String), this may not be so bad.
|
293
293
|
|
294
|
-
- On an
|
294
|
+
- On an 1.3GHz Core 2 Duo under linux, with debugging turned off (-b option),
|
295
295
|
test-concurrency.rb reports:
|
296
296
|
|
297
297
|
processes | threads | objects | transactions per cpu second
|
298
298
|
--------- | --------- | --------- | ---------------------------
|
299
|
-
1 | 1 | 10 |
|
300
|
-
1 | 10 | 10 |
|
301
|
-
10 | 1 | 10 |
|
302
|
-
10 | 10 | 10 |
|
303
|
-
10 | 10 | 100 |
|
304
|
-
10 | 10 | 10000 |
|
299
|
+
1 | 1 | 10 | 4798
|
300
|
+
1 | 10 | 10 | 3537
|
301
|
+
10 | 1 | 10 | 4231
|
302
|
+
10 | 10 | 10 | 4093
|
303
|
+
10 | 10 | 100 | 4060
|
304
|
+
10 | 10 | 10000 | 3700
|
305
305
|
|
306
306
|
These results are not representative of typical applications, because the
|
307
307
|
test was designed to stress the database and expose stability problems, not
|
@@ -521,22 +521,6 @@ is:
|
|
521
521
|
|
522
522
|
### Performance
|
523
523
|
|
524
|
-
- Profiling says that Thread.exclusive consumes about 20% of cpu. Also,
|
525
|
-
Thread.stop and Thread.run when there are multiple threads. Using
|
526
|
-
Thread.critical in places where it is safe to do so (no exceptions raised)
|
527
|
-
instead of Thread.exclusive would reduce this to an estimated 6%.
|
528
|
-
((See faster-modex .rb and faster-mutex.rb.))
|
529
|
-
|
530
|
-
- Better way of waiting for file lock in the multithread case
|
531
|
-
|
532
|
-
- this may be unfixable until ruby has native threads
|
533
|
-
|
534
|
-
- Use shared memory for the cache, so write is not necessary after edit.
|
535
|
-
|
536
|
-
- actually, this may not make much sense
|
537
|
-
|
538
|
-
- Option for Database to ignore file locking and possibility of other writers.
|
539
|
-
|
540
524
|
- fetch could use the cache better if the cache kept the file contents string
|
541
525
|
as well as the loaded object. Then the #stale! call would only have to
|
542
526
|
wipe the reference to the object, and could leave the contents string. But
|
Binary file
|
data/examples/fixture.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'fsdb'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
data = FSDB::Database.new "data"
|
5
|
+
|
6
|
+
formats = [
|
7
|
+
FSDB::TEXT_FORMAT.when(/\.txt$|\.html$/),
|
8
|
+
FSDB::BINARY_FORMAT.when(/\.png$/)
|
9
|
+
]
|
10
|
+
|
11
|
+
data.formats = formats
|
12
|
+
|
13
|
+
Dir.mktmpdir do |dir|
|
14
|
+
#dir = "tmp1" # for testing, use a dir that won't be deleted
|
15
|
+
|
16
|
+
tmp = FSDB::Database.new(dir)
|
17
|
+
tmp.formats = formats
|
18
|
+
|
19
|
+
tmp["hello.txt"] = "contents of hello.txt"
|
20
|
+
tmp["web/snippet.html"] = "<h1>Fix this!</h1>"
|
21
|
+
|
22
|
+
img = tmp.subdb("img")
|
23
|
+
img["hello.png"] = data["hello.png"]
|
24
|
+
img["hi.png"] = data["hello.png"]
|
25
|
+
end
|
26
|
+
|
27
|
+
# you can automatically serialize objects in yaml, json, or marshal:
|
28
|
+
require 'yaml'
|
29
|
+
require 'json'
|
30
|
+
|
31
|
+
Dir.mktmpdir do |dir|
|
32
|
+
#dir = "tmp2" # for testing, use a dir that won't be deleted
|
33
|
+
|
34
|
+
tmp = FSDB::Database.new(dir)
|
35
|
+
json_format = FSDB::Format.new(
|
36
|
+
/\.json$/, /\.js$/,
|
37
|
+
:name => "JSON_FORMAT",
|
38
|
+
:load => proc {|f| JSON.load(f)},
|
39
|
+
:dump => proc {|object, f| f.syswrite(JSON.dump(object))}
|
40
|
+
)
|
41
|
+
tmp.formats = formats + [FSDB::YAML_FORMAT, json_format]
|
42
|
+
|
43
|
+
tmp["a.yaml"] = {:some => ["complex", Object]}
|
44
|
+
tmp["b.json"] = ["foo", 2, 3]
|
45
|
+
end
|
data/lib/fsdb/database.rb
CHANGED
@@ -85,12 +85,14 @@ class Database
|
|
85
85
|
@object = object
|
86
86
|
end
|
87
87
|
|
88
|
-
|
89
|
-
def
|
88
|
+
# called in context of lock on db's cache_mutex.
|
89
|
+
def start_using; @users += 1; end
|
90
90
|
|
91
|
-
# called in context of lock on db's cache_mutex
|
92
|
-
|
93
|
-
|
91
|
+
# called in context of lock on db's cache_mutex.
|
92
|
+
def stop_using; @users -= 1; end
|
93
|
+
|
94
|
+
# called in context of lock on db's cache_mutex.
|
95
|
+
def unused?; @users == 0; end
|
94
96
|
|
95
97
|
# Protects object during #browse, #edit, and so on. Should be locked
|
96
98
|
# as long as the object is being used. It's ok to lock the @mutex
|
@@ -160,6 +162,8 @@ class Database
|
|
160
162
|
|
161
163
|
# Create a new database object that accesses +path+ relative to the database
|
162
164
|
# directory. A process can have any number of dbs accessing overlapping dirs.
|
165
|
+
# The FSDB concurrency protections apply to a file regardless of which db
|
166
|
+
# is used to access it.
|
163
167
|
# The cost of creating an additional db is very low; its state is just the
|
164
168
|
# dir and some options. Caching is done in structures owned by the Database
|
165
169
|
# class itself.
|
@@ -174,10 +178,11 @@ class Database
|
|
174
178
|
def inspect; "#<#{self.class}:#{dir}>"; end
|
175
179
|
|
176
180
|
# Convert a relative path (relative to the db dir) to an absolute path.
|
181
|
+
# A directory path will have '/' appended to it.
|
177
182
|
def absolute(path)
|
178
183
|
abs_path = File.expand_path(File.join(@dir, path))
|
179
184
|
if File.directory?(abs_path)
|
180
|
-
abs_path <<
|
185
|
+
abs_path << "/" # prevent Errno::EINVAL on UFS
|
181
186
|
end
|
182
187
|
abs_path
|
183
188
|
end
|
@@ -346,7 +351,11 @@ private
|
|
346
351
|
end
|
347
352
|
yield cache_entry
|
348
353
|
ensure
|
349
|
-
|
354
|
+
if cache_entry
|
355
|
+
cache_mutex.synchronize do
|
356
|
+
cache_entry.stop_using
|
357
|
+
end
|
358
|
+
end
|
350
359
|
end
|
351
360
|
|
352
361
|
# Lock path for shared (read) use. Other threads will wait to modify it.
|
@@ -376,7 +385,7 @@ private
|
|
376
385
|
rescue Errno::EINTR
|
377
386
|
retry
|
378
387
|
else
|
379
|
-
f.
|
388
|
+
f.lock_shared_fsdb(@lock_type)
|
380
389
|
identify_file_type(f, path, abs_path)
|
381
390
|
yield f
|
382
391
|
ensure
|
@@ -398,7 +407,7 @@ private
|
|
398
407
|
rescue Errno::EINTR
|
399
408
|
retry
|
400
409
|
else
|
401
|
-
f.
|
410
|
+
f.lock_exclusive_fsdb(@lock_type)
|
402
411
|
identify_file_type(f, path, abs_path)
|
403
412
|
yield f
|
404
413
|
ensure
|
@@ -493,7 +502,7 @@ public
|
|
493
502
|
end
|
494
503
|
|
495
504
|
cache_entry.file_handle = f
|
496
|
-
f.
|
505
|
+
f.lock_shared_fsdb(@lock_type)
|
497
506
|
identify_file_type(f, path, abs_path)
|
498
507
|
## could avoid if cache_object says so
|
499
508
|
object = cache_object(f, cache_entry)
|
@@ -615,18 +624,14 @@ public
|
|
615
624
|
# Insert the object, replacing anything at the path. Returns the object.
|
616
625
|
# (The object remains a <i>local copy</i>, distinct from the one which will be
|
617
626
|
# returned when accessing the path through database transactions.)
|
618
|
-
#
|
619
|
-
# If +path+ ends in "/", then object is treated as a collection of key-value
|
620
|
-
# pairs, and each value is inserted at the corresponding key under +path+.
|
621
|
-
# (You can omit the "/" if the dir already exists.)
|
622
|
-
### is this still true?
|
623
627
|
def insert(path, object)
|
624
628
|
abs_path = absolute(path)
|
625
629
|
file_id = make_file_id(abs_path)
|
626
630
|
object_exclusive file_id do |cache_entry|
|
627
631
|
open_write_lock(path) do |f|
|
628
632
|
dump(object, f)
|
629
|
-
|
633
|
+
inc_version_of(f, cache_entry)
|
634
|
+
cache_entry.stale!
|
630
635
|
object
|
631
636
|
end
|
632
637
|
end
|
@@ -647,7 +652,7 @@ public
|
|
647
652
|
rescue MissingFileError
|
648
653
|
raise DirIsImmutableError if PLATFORM_IS_WINDOWS_ME
|
649
654
|
ensure
|
650
|
-
clear_entry(file_id) #
|
655
|
+
clear_entry(file_id) # the entry was recently marked stale anyway
|
651
656
|
end
|
652
657
|
alias []= insert
|
653
658
|
|
@@ -750,6 +755,8 @@ public
|
|
750
755
|
raise unless File.directory?(abs_path)
|
751
756
|
# on some platforms, opening a dir raises EACCESS
|
752
757
|
return Formats::DIR_LOAD_FROM_PATH[abs_path]
|
758
|
+
ensure
|
759
|
+
clear_entry(file_id) # the entry was recently marked stale anyway
|
753
760
|
end
|
754
761
|
alias [] fetch
|
755
762
|
|
data/lib/fsdb/file-lock.rb
CHANGED
@@ -10,17 +10,17 @@ class File
|
|
10
10
|
if FSDB::PLATFORM_IS_WINDOWS_ME
|
11
11
|
# no flock() on WinME
|
12
12
|
|
13
|
-
def
|
13
|
+
def lock_exclusive_fsdb lock_type # :nodoc
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def lock_shared_fsdb lock_type # :nodoc
|
17
17
|
end
|
18
18
|
|
19
19
|
else
|
20
20
|
# Get an exclusive (i.e., write) lock on the file.
|
21
21
|
# If the lock is not available, wait for it without blocking other ruby
|
22
22
|
# threads.
|
23
|
-
def
|
23
|
+
def lock_exclusive_fsdb lock_type
|
24
24
|
send(lock_type, LOCK_EX)
|
25
25
|
rescue Errno::EINTR
|
26
26
|
retry
|
@@ -29,7 +29,7 @@ class File
|
|
29
29
|
# Get a shared (i.e., read) lock on the file.
|
30
30
|
# If the lock is not available, wait for it without blocking other ruby
|
31
31
|
# threads.
|
32
|
-
def
|
32
|
+
def lock_shared_fsdb lock_type
|
33
33
|
send(lock_type, LOCK_SH)
|
34
34
|
rescue Errno::EINTR
|
35
35
|
retry
|
data/lib/fsdb/modex.rb
CHANGED
data/lib/fsdb/persistent.rb
CHANGED
@@ -27,7 +27,7 @@ module Persistent
|
|
27
27
|
persistent_mutex.synchronize do
|
28
28
|
File.makedirs(File.dirname(persistent_file))
|
29
29
|
File.open(persistent_file, "wb") do |f|
|
30
|
-
f.
|
30
|
+
f.lock_exclusive_fsdb do
|
31
31
|
dump(f)
|
32
32
|
yield self if block_given?
|
33
33
|
end
|
@@ -50,7 +50,7 @@ module Persistent
|
|
50
50
|
# a particular object, or there will be multiple copies.
|
51
51
|
def restore file
|
52
52
|
object = File.open(file, "rb") do |f|
|
53
|
-
f.
|
53
|
+
f.lock_shared_fsdb do
|
54
54
|
load(f)
|
55
55
|
end
|
56
56
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fsdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-05-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'A file system data base. Provides a thread-safe, process-safe Database
|
15
15
|
class.
|
@@ -30,6 +30,8 @@ files:
|
|
30
30
|
- README.markdown
|
31
31
|
- bench/bench.rb
|
32
32
|
- examples/indexes.rb
|
33
|
+
- examples/fixture.rb
|
34
|
+
- examples/data/hello.png
|
33
35
|
- examples/rbformat.rb
|
34
36
|
- examples/formats.rb
|
35
37
|
- examples/yaml.rb
|
@@ -88,8 +90,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
90
|
version: '0'
|
89
91
|
requirements: []
|
90
92
|
rubyforge_project: fsdb
|
91
|
-
rubygems_version: 1.8.
|
93
|
+
rubygems_version: 1.8.24
|
92
94
|
signing_key:
|
93
95
|
specification_version: 3
|
94
96
|
summary: File System Database
|
95
97
|
test_files: []
|
98
|
+
has_rdoc:
|