fsdb 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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 850MHz PIII under linux, with debugging turned off (-b option),
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 | 965
300
- 1 | 10 | 10 | 165
301
- 10 | 1 | 10 | 684
302
- 10 | 10 | 10 | 122
303
- 10 | 10 | 100 | 100
304
- 10 | 10 | 10000 | 92
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
@@ -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
- def start_using; Thread.exclusive {@users += 1}; end
89
- def stop_using; Thread.exclusive {@users -= 1}; end
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, which is also
92
- # required for start_using.
93
- def unused?; @users == 0; end
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 << ?/ # prevent Errno::EINVAL on UFS
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
- cache_entry.stop_using if cache_entry
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.lock_shared(@lock_type)
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.lock_exclusive(@lock_type)
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.lock_shared(@lock_type)
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
- cache_entry.update(f.mtime, inc_version_of(f, cache_entry), object)
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) # no one else can get this copy of object
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
 
@@ -10,17 +10,17 @@ class File
10
10
  if FSDB::PLATFORM_IS_WINDOWS_ME
11
11
  # no flock() on WinME
12
12
 
13
- def lock_exclusive lock_type # :nodoc
13
+ def lock_exclusive_fsdb lock_type # :nodoc
14
14
  end
15
15
 
16
- def lock_shared lock_type # :nodoc
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 lock_exclusive lock_type
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 lock_shared lock_type
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
@@ -1,3 +1,5 @@
1
+ require 'thread'
2
+
1
3
  module FSDB
2
4
 
3
5
  # Modex is a modal exclusion semaphore.
@@ -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.lock_exclusive do
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.lock_shared do
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.1
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: 2011-12-06 00:00:00.000000000 Z
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.11
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: