fsdb 0.5 → 0.6.0

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.
Files changed (176) hide show
  1. data/{RELEASE-NOTES → History.txt} +6 -0
  2. data/{README → README.txt} +26 -17
  3. data/examples/flat.rb +146 -0
  4. data/examples/fsdb-example.rb +28 -0
  5. data/examples/rbformat.rb +17 -0
  6. data/examples/yaml2.rb +29 -0
  7. data/junk/OLDRakefile +98 -0
  8. data/junk/OLDRakefile2 +55 -0
  9. data/junk/check-cache.rb +18 -0
  10. data/junk/create-lock.rb +25 -0
  11. data/junk/doc/old-api/classes/FSDB.html +139 -0
  12. data/junk/doc/old-api/classes/FSDB/Database.html +953 -0
  13. data/junk/doc/old-api/classes/FSDB/Database.src/M000029.html +16 -0
  14. data/junk/doc/old-api/classes/FSDB/Database.src/M000030.html +16 -0
  15. data/junk/doc/old-api/classes/FSDB/Database.src/M000031.html +16 -0
  16. data/junk/doc/old-api/classes/FSDB/Database.src/M000032.html +16 -0
  17. data/junk/doc/old-api/classes/FSDB/Database.src/M000033.html +33 -0
  18. data/junk/doc/old-api/classes/FSDB/Database.src/M000034.html +18 -0
  19. data/junk/doc/old-api/classes/FSDB/Database.src/M000035.html +22 -0
  20. data/junk/doc/old-api/classes/FSDB/Database.src/M000036.html +16 -0
  21. data/junk/doc/old-api/classes/FSDB/Database.src/M000037.html +22 -0
  22. data/junk/doc/old-api/classes/FSDB/Database.src/M000038.html +43 -0
  23. data/junk/doc/old-api/classes/FSDB/Database.src/M000039.html +25 -0
  24. data/junk/doc/old-api/classes/FSDB/Database.src/M000040.html +43 -0
  25. data/junk/doc/old-api/classes/FSDB/Database.src/M000041.html +23 -0
  26. data/junk/doc/old-api/classes/FSDB/Database.src/M000042.html +22 -0
  27. data/junk/doc/old-api/classes/FSDB/Database.src/M000043.html +16 -0
  28. data/junk/doc/old-api/classes/FSDB/Database.src/M000044.html +16 -0
  29. data/junk/doc/old-api/classes/FSDB/Database.src/M000045.html +18 -0
  30. data/junk/doc/old-api/classes/FSDB/Database.src/M000046.html +18 -0
  31. data/junk/doc/old-api/classes/FSDB/Database.src/M000047.html +18 -0
  32. data/junk/doc/old-api/classes/FSDB/Database.src/M000048.html +16 -0
  33. data/junk/doc/old-api/classes/FSDB/Database.src/M000049.html +71 -0
  34. data/junk/doc/old-api/classes/FSDB/Database.src/M000050.html +43 -0
  35. data/junk/doc/old-api/classes/FSDB/Database.src/M000051.html +53 -0
  36. data/junk/doc/old-api/classes/FSDB/Database.src/M000052.html +44 -0
  37. data/junk/doc/old-api/classes/FSDB/Database.src/M000053.html +39 -0
  38. data/junk/doc/old-api/classes/FSDB/Database.src/M000054.html +72 -0
  39. data/junk/doc/old-api/classes/FSDB/Database.src/M000055.html +39 -0
  40. data/junk/doc/old-api/classes/FSDB/Database.src/M000056.html +18 -0
  41. data/junk/doc/old-api/classes/FSDB/Database.src/M000057.html +18 -0
  42. data/junk/doc/old-api/classes/FSDB/Database.src/M000058.html +18 -0
  43. data/junk/doc/old-api/classes/FSDB/Database.src/M000059.html +18 -0
  44. data/junk/doc/old-api/classes/FSDB/Database.src/M000060.html +18 -0
  45. data/junk/doc/old-api/classes/FSDB/Database.src/M000061.html +23 -0
  46. data/junk/doc/old-api/classes/FSDB/Database.src/M000062.html +23 -0
  47. data/junk/doc/old-api/classes/FSDB/Database.src/M000063.html +18 -0
  48. data/junk/doc/old-api/classes/FSDB/Database.src/M000064.html +18 -0
  49. data/junk/doc/old-api/classes/FSDB/Database/AbortedTransaction.html +118 -0
  50. data/junk/doc/old-api/classes/FSDB/Database/CreateFileError.html +120 -0
  51. data/junk/doc/old-api/classes/FSDB/Database/DirIsImmutableError.html +117 -0
  52. data/junk/doc/old-api/classes/FSDB/Database/DirNotEmptyError.html +117 -0
  53. data/junk/doc/old-api/classes/FSDB/Database/FormatError.html +117 -0
  54. data/junk/doc/old-api/classes/FSDB/Database/MissingFileError.html +117 -0
  55. data/junk/doc/old-api/classes/FSDB/Database/MissingObjectError.html +117 -0
  56. data/junk/doc/old-api/classes/FSDB/Database/NotDirError.html +118 -0
  57. data/junk/doc/old-api/classes/FSDB/Database/PathComponentError.html +120 -0
  58. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.html +148 -0
  59. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000005.html +21 -0
  60. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000007.html +21 -0
  61. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.html +210 -0
  62. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000006.html +22 -0
  63. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000007.html +22 -0
  64. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000008.html +22 -0
  65. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000009.html +22 -0
  66. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000010.html +22 -0
  67. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000011.html +22 -0
  68. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000012.html +22 -0
  69. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000013.html +22 -0
  70. data/junk/doc/old-api/classes/FSDB/ForkSafely.html +126 -0
  71. data/junk/doc/old-api/classes/FSDB/Modex.html +237 -0
  72. data/junk/doc/old-api/classes/FSDB/Modex.src/M000024.html +21 -0
  73. data/junk/doc/old-api/classes/FSDB/Modex.src/M000025.html +30 -0
  74. data/junk/doc/old-api/classes/FSDB/Modex.src/M000026.html +21 -0
  75. data/junk/doc/old-api/classes/FSDB/Modex.src/M000027.html +30 -0
  76. data/junk/doc/old-api/classes/FSDB/Modex.src/M000028.html +44 -0
  77. data/junk/doc/old-api/classes/FSDB/Modex.src/M000029.html +26 -0
  78. data/junk/doc/old-api/classes/FSDB/Modex.src/M000030.html +48 -0
  79. data/junk/doc/old-api/classes/FSDB/Modex/ForkSafely.html +105 -0
  80. data/junk/doc/old-api/classes/FSDB/Mutex.html +244 -0
  81. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000018.html +19 -0
  82. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000019.html +18 -0
  83. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000020.html +19 -0
  84. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000021.html +18 -0
  85. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000022.html +23 -0
  86. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000023.html +30 -0
  87. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000024.html +26 -0
  88. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000025.html +21 -0
  89. data/junk/doc/old-api/classes/FSDB/Mutex/ForkSafely.html +105 -0
  90. data/junk/doc/old-api/classes/FSDB/PathUtilities.html +257 -0
  91. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000012.html +23 -0
  92. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000013.html +18 -0
  93. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000014.html +23 -0
  94. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000015.html +18 -0
  95. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000016.html +18 -0
  96. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000017.html +22 -0
  97. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000018.html +23 -0
  98. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000019.html +18 -0
  99. data/junk/doc/old-api/classes/FSDB/PathUtilities/InvalidPathError.html +111 -0
  100. data/junk/doc/old-api/classes/File.html +272 -0
  101. data/junk/doc/old-api/classes/File.src/M000001.html +17 -0
  102. data/junk/doc/old-api/classes/File.src/M000002.html +17 -0
  103. data/junk/doc/old-api/classes/File.src/M000003.html +20 -0
  104. data/junk/doc/old-api/classes/File.src/M000004.html +20 -0
  105. data/junk/doc/old-api/classes/File.src/M000005.html +32 -0
  106. data/junk/doc/old-api/classes/File.src/M000006.html +32 -0
  107. data/junk/doc/old-api/created.rid +1 -0
  108. data/junk/doc/old-api/files/README.html +112 -0
  109. data/junk/doc/old-api/files/RELEASE-NOTES.html +233 -0
  110. data/junk/doc/old-api/files/fsdb_txt.html +888 -0
  111. data/junk/doc/old-api/files/lib/fsdb/database_rb.html +115 -0
  112. data/junk/doc/old-api/files/lib/fsdb/file-lock_rb.html +109 -0
  113. data/junk/doc/old-api/files/lib/fsdb/modex_rb.html +121 -0
  114. data/junk/doc/old-api/files/lib/fsdb/mutex_rb.html +108 -0
  115. data/junk/doc/old-api/files/lib/fsdb/util_rb.html +108 -0
  116. data/junk/doc/old-api/fr_class_index.html +47 -0
  117. data/junk/doc/old-api/fr_file_index.html +34 -0
  118. data/junk/doc/old-api/fr_method_index.html +90 -0
  119. data/junk/doc/old-api/index.html +24 -0
  120. data/junk/doc/old-api/rdoc-style.css +208 -0
  121. data/junk/file-lock-nb.rb +15 -0
  122. data/junk/fl.rb +144 -0
  123. data/junk/flock-test.rb +39 -0
  124. data/junk/fsdb.kateproject +47 -0
  125. data/junk/fsdb.prj +196 -0
  126. data/junk/fsdb.sf +46 -0
  127. data/junk/insert-dir.rb +48 -0
  128. data/junk/lock-test-bug.rb +150 -0
  129. data/junk/lock-test-too-simple.rb +136 -0
  130. data/junk/lock-test.rb +151 -0
  131. data/{script → junk}/mkrdoc +0 -0
  132. data/junk/restore-fsdb.rb +37 -0
  133. data/junk/rf.txt +5 -0
  134. data/junk/solaris-bug-fixed.rb +184 -0
  135. data/junk/solaris-bug.rb +182 -0
  136. data/junk/solaris-bug.txt +43 -0
  137. data/junk/sync.rb +327 -0
  138. data/junk/test-file-lock.html +86 -0
  139. data/junk/test-file-lock.rb +84 -0
  140. data/junk/test-processes.rb +131 -0
  141. data/junk/test-threads.rb +113 -0
  142. data/junk/wiki-mutex.rb +108 -0
  143. data/lib/fsdb/database.rb +5 -3
  144. data/lib/fsdb/delegatable.rb +21 -0
  145. data/lib/fsdb/faster-modex.rb +223 -0
  146. data/lib/fsdb/faster-mutex.rb +138 -0
  147. data/lib/fsdb/mutex.rb +4 -1
  148. data/lib/fsdb/persistent.rb +91 -0
  149. data/lib/fsdb/read-write-object.rb +36 -0
  150. data/lib/fsdb/server.rb +44 -0
  151. data/misc/fsdb-blorubu.txt +47 -0
  152. data/misc/mtime-and-file-id.txt +23 -0
  153. data/misc/posixlock.txt +148 -0
  154. data/rakefile +39 -0
  155. data/tasks/ann.rake +80 -0
  156. data/tasks/bones.rake +20 -0
  157. data/tasks/gem.rake +201 -0
  158. data/tasks/git.rake +40 -0
  159. data/tasks/notes.rake +27 -0
  160. data/tasks/post_load.rake +34 -0
  161. data/tasks/rdoc.rake +51 -0
  162. data/tasks/rubyforge.rake +55 -0
  163. data/tasks/setup.rb +292 -0
  164. data/tasks/spec.rake +54 -0
  165. data/tasks/svn.rake +47 -0
  166. data/tasks/test.rake +40 -0
  167. data/tasks/zentest.rake +36 -0
  168. data/test/err.txt +31 -0
  169. data/test/runs.rb +8 -0
  170. data/test/test-file-lock.rb +78 -0
  171. data/test/test-util.rb +1 -0
  172. data/test/trap.rb +31 -0
  173. metadata +198 -35
  174. data/Manifest +0 -36
  175. data/Rakefile +0 -10
  176. data/fsdb.gemspec +0 -113
@@ -0,0 +1,888 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
+
6
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
+ <head>
8
+ <title>File: fsdb.txt</title>
9
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
11
+ <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
12
+ <script type="text/javascript">
13
+ // <![CDATA[
14
+
15
+ function popupCode( url ) {
16
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
17
+ }
18
+
19
+ function toggleCode( id ) {
20
+ if ( document.getElementById )
21
+ elem = document.getElementById( id );
22
+ else if ( document.all )
23
+ elem = eval( "document.all." + id );
24
+ else
25
+ return false;
26
+
27
+ elemStyle = elem.style;
28
+
29
+ if ( elemStyle.display != "block" ) {
30
+ elemStyle.display = "block"
31
+ } else {
32
+ elemStyle.display = "none"
33
+ }
34
+
35
+ return true;
36
+ }
37
+
38
+ // Make codeblocks hidden by default
39
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
40
+
41
+ // ]]>
42
+ </script>
43
+
44
+ </head>
45
+ <body>
46
+
47
+
48
+
49
+ <div id="fileHeader">
50
+ <h1>fsdb.txt</h1>
51
+ <table class="header-table">
52
+ <tr class="top-aligned-row">
53
+ <td><strong>Path:</strong></td>
54
+ <td>fsdb.txt
55
+ </td>
56
+ </tr>
57
+ <tr class="top-aligned-row">
58
+ <td><strong>Last Update:</strong></td>
59
+ <td>Tue Mar 14 11:20:07 PST 2006</td>
60
+ </tr>
61
+ </table>
62
+ </div>
63
+ <!-- banner header -->
64
+
65
+ <div id="bodyContent">
66
+
67
+
68
+
69
+ <div id="contextContent">
70
+
71
+ <div id="description">
72
+ <h1>What is <a href="../classes/FSDB.html">FSDB</a>?</h1>
73
+ <p>
74
+ <a href="../classes/FSDB.html">FSDB</a> is a file system data base. <a
75
+ href="../classes/FSDB.html">FSDB</a> provides a thread-safe, process-safe
76
+ Database class which uses the native file system as its back end and allows
77
+ multiple file formats and serialization methods. Users access objects in
78
+ terms of their paths relative to the base directory of the database.
79
+ It&#8217;s very light weight (the state of a Database is essentially just a
80
+ path string, and code size is very small, under 1K lines, all ruby).
81
+ </p>
82
+ <p>
83
+ <a href="../classes/FSDB.html">FSDB</a> stores bundles of ruby objects at
84
+ nodes in the file system. Each bundle is saved and restored as a whole, so
85
+ internal references persist as usual. These bundles are the atoms of
86
+ transactions. References between bundles are handled through path strings.
87
+ The format of each bundle on disk can vary; format classes for plain text
88
+ strings, marshalled data, and yaml data are included, but <a
89
+ href="../classes/FSDB.html">FSDB</a> can easily be extended to recognize
90
+ other formats, both binary and text. <a
91
+ href="../classes/FSDB.html">FSDB</a> treats directories as collections and
92
+ provides directory iterator methods.
93
+ </p>
94
+ <p>
95
+ <a href="../classes/FSDB.html">FSDB</a> has been tested on a variety of
96
+ platforms and ruby versions, and is not known to have any problems. (On
97
+ WindowsME/98/95, multiple processes can access a database unsafely, because
98
+ flock() is not available on the platform.) See the Testing section for
99
+ details.
100
+ </p>
101
+ <p>
102
+ <a href="../classes/FSDB.html">FSDB</a> does not yet have any indexing or
103
+ querying mechanisms, and is probably missing many other useful database
104
+ features, so it is not a general replacement for RDBs or OODBs. However, if
105
+ you are looking for a lightweight, concurrent object store with reasonable
106
+ performance and better granularity than PStore, in pure Ruby, with a Ruby
107
+ license, take a look at <a href="../classes/FSDB.html">FSDB</a>. Also, if
108
+ you are looking for an easy way of making an existing file tree look like a
109
+ database, especially if it has heterogeneous file formats, <a
110
+ href="../classes/FSDB.html">FSDB</a> might be useful.
111
+ </p>
112
+ <h2>Installation</h2>
113
+ <pre>
114
+ ruby install.rb config
115
+ ruby install.rb setup
116
+ ruby install.rb install
117
+ </pre>
118
+ <h2>Synopsis</h2>
119
+ <pre>
120
+ require 'fsdb'
121
+
122
+ db = FSDB::Database.new('/tmp/my-data')
123
+
124
+ db['recent-movies/myself'] = [&quot;LOTR II&quot;, &quot;Austin Powers&quot;]
125
+ puts db['recent-movies/myself'][0] # ==&gt; &quot;LOTR II&quot;
126
+
127
+ db.edit 'recent-movies/myself' do |list|
128
+ list &lt;&lt; &quot;A la recherche du temps perdu&quot;
129
+ end
130
+ </pre>
131
+ <h2>Path names</h2>
132
+ <p>
133
+ Keys in the database are path strings, which are simply strings in the
134
+ usual forward-slash delimited format, relative to the database&#8217;s
135
+ directory. There are some points to be aware of when using them to refer to
136
+ database objects.
137
+ </p>
138
+ <ul>
139
+ <li>Paths to directories are formed in one of two ways:
140
+
141
+ <ul>
142
+ <li>explicitly, with a trailing slash, as in <tt>db[&#8216;foo/&#8217;]</tt>
143
+
144
+ </li>
145
+ <li>implicitly, as in <tt>db[&#8216;foo&#8217;]</tt> if foo is already a
146
+ directory, or as in <tt>db[&#8216;foo/bar&#8217;]</tt>, which creates
147
+ <tt>&#8216;foo&#8217;</tt> if it did not already exist.
148
+
149
+ </li>
150
+ </ul>
151
+ <p>
152
+ The root dir of the database is simply <tt>/</tt>, its child directories
153
+ are of the form <tt>foo/</tt> and so on. The leading and trailing slashes
154
+ are both optional.
155
+ </p>
156
+ </li>
157
+ <li>Objects can be stored in various formats, indicated by path name. A typical
158
+ mapping might be:
159
+
160
+ <table>
161
+ <tr><td valign="top"><tt>foo.obj</tt>:</td><td>Marshalled data (the default for unrecognized extension)
162
+
163
+ </td></tr>
164
+ <tr><td valign="top"><tt>foo.txt</tt>:</td><td>String
165
+
166
+ </td></tr>
167
+ <tr><td valign="top"><tt>foo/</tt>:</td><td>Directory (the contents is presented to the caller as a list of file and
168
+ subdirectory paths that can be used in browse, edit, etc.)
169
+
170
+ </td></tr>
171
+ <tr><td valign="top"><tt>foo.yml</tt>:</td><td>YAML data&#8212;see examples/yaml.rb
172
+
173
+ </td></tr>
174
+ </table>
175
+ <p>
176
+ New formats, which correlate filename pattern with serialization behavior,
177
+ can be defined and plugged in to databases. Each format has its own rules
178
+ for matching patterns in the file name and recognizing the file. Patterns
179
+ can be anything with a #=== method (such as a regex). See
180
+ lib/fsdb/formats.rb examples of defining formats. For examples of
181
+ associating formats with patterns, see examples/formats.rb.
182
+ </p>
183
+ </li>
184
+ <li>Different notations for the same path, such as
185
+
186
+ <pre>
187
+ /foo/bar
188
+ foo/bar
189
+ foo//bar
190
+ foo/../foo/bar
191
+ </pre>
192
+ <p>
193
+ work correctly, as do paths that denote hard or soft links, if supported on
194
+ the platform.
195
+ </p>
196
+ <p>
197
+ Links are subject to the same naming convention as normal files with regard
198
+ to format identification: format is determined by the path within the
199
+ database used to access the object. Using a different name for a link can
200
+ be useful if you need to access the file using two different formats (e.g.,
201
+ plain text via &#8216;foo.txt&#8217; and tabular data via
202
+ &#8216;foo.table&#8217; or whatever).
203
+ </p>
204
+ </li>
205
+ <li>Accessing objects in a database is unaffected by the current dir of your
206
+ process. The database knows it&#8217;s own absolute path, and path
207
+ arguments to the Database API are interpreted relative to that. If you want
208
+ to work with a subdirectory of the database, and paths relative to that,
209
+ use Database#subdb:
210
+
211
+ <pre>
212
+ db = Database.new['/tmp']
213
+ db['foo/bar'] = 1
214
+ foo = db.subdb('foo')
215
+ foo['bar'] # ==&gt; 1
216
+ </pre>
217
+ </li>
218
+ <li>Paths that are outside the database (&#8217;../../zap&#8217;) are allowed,
219
+ but may or may not be desirable. Use valid? and validate in util.rb to
220
+ check for them.
221
+
222
+ </li>
223
+ <li>Directories are created when needed. So db[&#8216;a/b/c&#8217;] = 1 creates
224
+ two dirs and one file.
225
+
226
+ </li>
227
+ <li>Files beginning with &#8217;..&#8217; are ignored by fsdb dir iterators,
228
+ though they can still be accessed in transaction operators. Some such files
229
+ (<tt>..fsdb.meta.&lt;filename&gt;</tt>) are used internally. All others
230
+ <em>not</em> beginning with <tt>..fsdb</tt> are reserved for applications
231
+ to use.
232
+
233
+ <p>
234
+ The <tt>..fsdb.meta.&lt;filename&gt;</tt> file holds a version number for
235
+ &lt;filename&gt;, which is used along with mtime to check for changes
236
+ (mtime usually has a precision of only 1 second). In the future, the file
237
+ may also be used to hold other metadata. (The meta file is only created
238
+ when a file is written to and does not need to be created in advance when
239
+ using existing files as a <a href="../classes/FSDB.html">FSDB</a>.)
240
+ </p>
241
+ </li>
242
+ <li>util.rb has directory iterators, path globbing, and other useful tools.
243
+
244
+ </li>
245
+ </ul>
246
+ <h2>Transactions</h2>
247
+ <p>
248
+ <a href="../classes/FSDB.html">FSDB</a> transactions are thread-safe and
249
+ process-safe. They can be nested for larger-grained transactions; it is the
250
+ user&#8217;s responsibility to avoid deadlock.
251
+ </p>
252
+ <p>
253
+ <a href="../classes/FSDB.html">FSDB</a> is ACID
254
+ (atomic/consistent/isolated/durable) to the extent that the underlying file
255
+ system is. For instance, when an object that has been modified in a
256
+ transaction is written to the file system, nothing persistent is changed
257
+ until the final system call to write the data to the OS&#8217;s buffers. If
258
+ there is an interruption (e.g., a power failure) while the OS flushes those
259
+ buffers to disk, data will not be consistent. If this bothers you, you may
260
+ want to use a journaling file system. <a
261
+ href="../classes/FSDB.html">FSDB</a> does not need to do its own journaling
262
+ because of the availability of good journaling file systems.
263
+ </p>
264
+ <p>
265
+ There are two kinds of transactions:
266
+ </p>
267
+ <ul>
268
+ <li>A simple transfer of a value, as in <tt>db[&#8216;x&#8217;]</tt> and
269
+ <tt>db[&#8216;x&#8217;] = 1</tt>.
270
+
271
+ <p>
272
+ Note that a sequence of such transactions is not itself a transaction, and
273
+ can be affected by other processes and threads.
274
+ </p>
275
+ <pre>
276
+ db['foo/bar'] = [1,2,3]
277
+ db['foo/bar'] += [4] # This is actually 2 transactions
278
+ db['foo/bar'][-1]
279
+ </pre>
280
+ <p>
281
+ It is possible for the result of these transactions to be <tt>4</tt>. But,
282
+ if other threads or processes are scheduled during this code fragment, the
283
+ result could be a completely different value, or the code could raise an
284
+ method-missing exception because the object at the path has been replaced
285
+ with one that does not have the <tt>+</tt> method or the <tt>[ ]</tt>
286
+ method. The four operations are atomic by themselves, but the sequence is
287
+ not.
288
+ </p>
289
+ <p>
290
+ Note that changes to a database object using this kind of transaction
291
+ cannot be made using destructive methods (such as <tt>&lt;&lt;</tt>) but
292
+ only by assignments of the form <tt>db[&lt;path&gt;] = &lt;data&gt;</tt>.
293
+ Note that <tt>+=</tt> and similar &quot;assignment operators&quot; can be
294
+ used but are not atomic, because
295
+ </p>
296
+ <pre>
297
+ db[&lt;path&gt;] += 1
298
+ </pre>
299
+ <p>
300
+ is really
301
+ </p>
302
+ <pre>
303
+ db[&lt;path&gt;] = db[&lt;path&gt;] + 1
304
+ </pre>
305
+ <p>
306
+ So another thread or process could change the value stored at <tt>path</tt>
307
+ while the addition is happening.
308
+ </p>
309
+ </li>
310
+ <li>Transactions that allow more complex interaction:
311
+
312
+ <pre>
313
+ path = 'foo/bar'
314
+ db[path] = [1,2,3]
315
+
316
+ db.edit path do |bar|
317
+ bar += [4]
318
+ bar[-1]
319
+ end
320
+ </pre>
321
+ <p>
322
+ This guarantees that, if the object at the path is still <tt>[1, 2, 3]</tt>
323
+ at the time of the edit call, the value returned by the transaction will be
324
+ +4+.
325
+ </p>
326
+ <p>
327
+ Simply put, edit allows exclusive write access to the object at the path
328
+ for the duration of the block. Other threads or processes that use <a
329
+ href="../classes/FSDB.html">FSDB</a> methods to read or write the object
330
+ will be blocked for the duration of the transaction. There is also browse,
331
+ which allows read access shared by any number of threads and processes, and
332
+ replace, which also allows exclusive write access like edit. The
333
+ differences between replace and edit are:
334
+ </p>
335
+ <ul>
336
+ <li>replace&#8217;s block must return the new value, whereas edit&#8217;s block
337
+ must operate (destructively) on the block argument to produce the new
338
+ value. (The new value in replace&#8217;s block can be a modification of the
339
+ old value, or an entirely different object.)
340
+
341
+ </li>
342
+ <li>replace yields <tt>nil</tt> if there is no preexisting object, whereas edit
343
+ calls default_edit (which by default calls object_missing, which by default
344
+ throws MissingObjectError).
345
+
346
+ </li>
347
+ <li>edit is useless over a drb connection, since is it operating on a
348
+ Marshal.dump-ed copy. Use replace with drb.
349
+
350
+ </li>
351
+ </ul>
352
+ <p>
353
+ You can delete an object from the database (and the file system) with
354
+ delete, which returns the object. Also, delete can take a block, which can
355
+ examine the object and abort the transaction to prevent deletion. (The
356
+ delete transaction has the same exclusion semantics as edit and replace.)
357
+ </p>
358
+ <p>
359
+ The fetch and insert methods are aliased with <tt>[ ]</tt> and <tt>[
360
+ ]=</tt>.
361
+ </p>
362
+ <p>
363
+ When the object at the path specified in a transaction does not exist in
364
+ the file system, the different transaction methods behave differently:
365
+ </p>
366
+ <ul>
367
+ <li>browse calls default_browse, which, in Database&#8217;s implementation,
368
+ calls object_missing, which raises Database::MissingObjectError.
369
+
370
+ </li>
371
+ <li>edit calls default_edit, which, in Database&#8217;s implementation, calls
372
+ object_missing, which raises Database::MissingObjectError.
373
+
374
+ </li>
375
+ <li>replace and insert (and #[]) ignore any missing file.
376
+
377
+ </li>
378
+ <li>delete does nothing (if you want, you can detect the fact that the object
379
+ is missing by checking for nil in the block argument).
380
+
381
+ </li>
382
+ <li>fetch calls default_fetch, which, in Database&#8217;s implementation,
383
+ returns nil.
384
+
385
+ </li>
386
+ </ul>
387
+ <p>
388
+ Transactions can be nested. However, the order in which objects are locked
389
+ can lead to deadlock if, for example, the nesting is cyclic, or two threads
390
+ or processes request the same set of locks in a different order. One
391
+ approach is to only request nested locks on paths in the lexicographic
392
+ order of the path strings: &quot;foo/bar&quot;, &quot;foo/baz&quot;,
393
+ &#8230;
394
+ </p>
395
+ <p>
396
+ A transaction can be aborted with Database#abort and Database.abort, after
397
+ which the state of the object in the database remains as before the
398
+ transaction. An exception that is raised but not handled within a
399
+ transaction also aborts the transaction.
400
+ </p>
401
+ <p>
402
+ Note that there is no locking on directories, but you can designate a lock
403
+ file for each dir and effectively have multiple-reader, single writer
404
+ (advisory) locking on dirs. Just make sure you enclose your dir operation
405
+ in a transaction on the lock object, and always access these objects using
406
+ this technique.
407
+ </p>
408
+ <pre>
409
+ db.browse('lock for dir') do
410
+ db['dir/x'] = 1
411
+ end
412
+ </pre>
413
+ </li>
414
+ </ul>
415
+ <h2>Guarding against concurrency problems</h2>
416
+ <ul>
417
+ <li>It&#8217;s the user&#8217;s responsibility to avoid deadlock. See above.
418
+
419
+ </li>
420
+ <li>If you want to fork from a multithreaded process, you should include <a
421
+ href="../classes/FSDB.html">FSDB</a> or <a
422
+ href="../classes/FSDB/ForkSafely.html">FSDB::ForkSafely</a>. This prevents
423
+ &quot;ghost&quot; threads in the child process from permanently holding
424
+ locks.
425
+
426
+ </li>
427
+ <li>It is not safe to fork while in a transaction.
428
+
429
+ </li>
430
+ <li>A database can be configured to use fcntl locking instead of ruby&#8217;s
431
+ usual flock call. This is necessary for Linux NFS, for example. There
432
+ doesn&#8217;t seem to be any performance difference when running on a local
433
+ filesystem.
434
+
435
+ </li>
436
+ </ul>
437
+ <h2>Limitations</h2>
438
+ <ul>
439
+ <li>Transactions are not journaled. There&#8217;s no commit, undo, or
440
+ versioning. (You can abort a transaction, however.) These could be
441
+ added&#8230;
442
+
443
+ </li>
444
+ </ul>
445
+ <h2>Testing</h2>
446
+ <p>
447
+ <a href="../classes/FSDB.html">FSDB</a> has been tested on the following
448
+ platforms and file systems:
449
+ </p>
450
+ <pre>
451
+ - Linux/x86 (single and dual cpu, ext3fs and reiserfs)
452
+
453
+ - Solaris/sparc (dual and quad cpu, nfs and ufs)
454
+
455
+ - QNX 6.2.1 (dual PIII)
456
+
457
+ - Windows 2000 (dual cpu, NTFS)
458
+
459
+ - Windows ME (single cpu, FAT32)
460
+ </pre>
461
+ <p>
462
+ <a href="../classes/FSDB.html">FSDB</a> is currently tested with ruby-1.9.0
463
+ and ruby-1.8.1.
464
+ </p>
465
+ <p>
466
+ On windows, both the mswin32 and mingw32 builds of ruby have been used with
467
+ <a href="../classes/FSDB.html">FSDB</a>. It has never been tested with
468
+ cygwin or bccwin.
469
+ </p>
470
+ <p>
471
+ The tests include unit and stress tests. Unit tests isolate individual
472
+ features of the library. The stress test (called test/test-concurrency.rb)
473
+ has many parameters, but typically involves several processes, each with
474
+ several threads, doing millions of transactions on a small set of objects.
475
+ </p>
476
+ <p>
477
+ The only known testing failure is on Windows ME (and presumably 95 and 98).
478
+ The stress test succeeds with one process and multiple threads. It succeeds
479
+ with multiple processes each with one thread. However, with two processes
480
+ each with two threads, the test usually deadlocks very quickly.
481
+ </p>
482
+ <h2>Performance</h2>
483
+ <p>
484
+ <a href="../classes/FSDB.html">FSDB</a> is not very fast. It&#8217;s useful
485
+ more for its safety, flexibility, and ease of use.
486
+ </p>
487
+ <ul>
488
+ <li><a href="../classes/FSDB.html">FSDB</a> operates on cached data as much as
489
+ possible. In order to be process safe, changing an object (with edit,
490
+ replace, insert) results in a dump of the object to the file system. This
491
+ includes marshalling or other custom serialization to a string, as well as
492
+ a syswrite call. The file system buffers may keep the latter part from
493
+ being too costly, but the former part can be costly, especially for complex
494
+ objects. By using either custom marshal methods, or nonpersistent attrs
495
+ where possible (see nonpersistent-attr.rb), or <a
496
+ href="../classes/FSDB.html">FSDB</a> dump/load methods that use a faster
497
+ format (e.g., plain text, rather than a marshalled String), this may not be
498
+ so bad.
499
+
500
+ </li>
501
+ <li>On an 850MHz PIII under linux, with debugging turned off (-b option),
502
+ test-concurrency.rb reports:
503
+
504
+ <pre>
505
+ processes threads objects transactions per cpu second
506
+ ---------------------------------------------------------------
507
+ 1 1 10 965
508
+ 1 10 10 165
509
+ 10 1 10 684
510
+ 10 10 10 122
511
+ 10 10 100 100
512
+ 10 10 10000 92
513
+ </pre>
514
+ <p>
515
+ These results are not representative of typical applications, because the
516
+ test was designed to stress the database and expose stability problems, not
517
+ to immitate typical use of database-stored objects. See bench/bench.rb for
518
+ for bechmarks.
519
+ </p>
520
+ </li>
521
+ <li>For speed, avoid using fetch and its alias #[]. As noted in the API docs,
522
+ these methods cannot safely return the same object that is cached, so must
523
+ clear out the cache&#8217;s reference to the object so that is will be
524
+ loaded freshly the next time fetch is called on the path.
525
+
526
+ <p>
527
+ The performance hit of fetch is of course greater with larger objects, and
528
+ with objects that are loaded by a more complex procedure, such as
529
+ Masrshal.load.
530
+ </p>
531
+ <p>
532
+ You can think of fetch as a &quot;deep copy&quot; of the object. If you
533
+ call it twice, you get different copies that do not share any parts. Or you
534
+ can think of it as File.read&#8212;it gives you an instantaneous snapshot
535
+ of the file, but does not give you a transaction &quot;window&quot; in
536
+ which no other thread or process can modify the object.
537
+ </p>
538
+ <p>
539
+ There is no analogous concern with insert and its alias #[]=. These methods
540
+ always write to the file system, but they also leave the object in the
541
+ cache.
542
+ </p>
543
+ </li>
544
+ <li>Performance is worse on Windows. Most of the delay seems to be in system,
545
+ rather than user, code.
546
+
547
+ </li>
548
+ </ul>
549
+ <h2>Advantages</h2>
550
+ <ul>
551
+ <li><a href="../classes/FSDB.html">FSDB</a> is useful with heterogeneous data,
552
+ that is, with files in varying formats that can be recognized based on file
553
+ name.
554
+
555
+ </li>
556
+ <li><a href="../classes/FSDB.html">FSDB</a> can be used as an interface to the
557
+ file system that understands file types. By defining new format clases,
558
+ it&#8217;s easy to set up databases that allow:
559
+
560
+ <pre>
561
+ home['.forward'] += [&quot;nobody@nowhere.net&quot;]
562
+ etc.edit('passwd') { |passwd| passwd['fred'].shell = '/bin/zsh' }
563
+ window.setIcon(icons['apps/editor.png'])
564
+ </pre>
565
+ </li>
566
+ <li>A <a href="../classes/FSDB.html">FSDB</a> can be operated on with ordinary
567
+ file tools. <a href="../classes/FSDB.html">FSDB</a> can even treat existing
568
+ file hierarchies as databases. It&#8217;s easy to backup, export, grep,
569
+ &#8230; the database. Its just files.
570
+
571
+ </li>
572
+ <li><a href="../classes/FSDB.html">FSDB</a> is process-safe, so it can be used
573
+ for <b>persistent</b>, *fault-tolerant* interprocess communication, such as
574
+ a queue that doesn&#8217;t require both processes to be alive at the same
575
+ time. It&#8217;s a good way to safely connect a suite of applications that
576
+ share common files. Also, you can take advantage of multiprocessor systems
577
+ by forking a new process to handle a CPU-intesive transaction.
578
+
579
+ </li>
580
+ <li><a href="../classes/FSDB.html">FSDB</a> is thread-safe, so it can be used
581
+ in a threaded server, such as drb. In fact, the <a
582
+ href="../classes/FSDB.html">FSDB</a> Database itself can be the drb server
583
+ object, allowing browse, replace (but not edit), insert, and delete from
584
+ remote clients! (See the examples server.rb and client.rb.)
585
+
586
+ </li>
587
+ <li><a href="../classes/FSDB.html">FSDB</a> can be used as a portable interface
588
+ to multithreaded file locking. (File#flock does not have consistent
589
+ semantics across platforms.)
590
+
591
+ </li>
592
+ <li>Compared with PStore, <a href="../classes/FSDB.html">FSDB</a> has the
593
+ potential for finer granularity, and it scales better. The cost of using
594
+ fine granularity is that referential structures, unless contained within
595
+ individual nodes, must be based on path strings. (But of course this would
596
+ be a problem with multiple PStores, as well.)
597
+
598
+ </li>
599
+ <li><a href="../classes/FSDB.html">FSDB</a> scales up to large numbers of
600
+ objects.
601
+
602
+ </li>
603
+ <li>Objects in a <a href="../classes/FSDB.html">FSDB</a> can be anything
604
+ serializable. They don&#8217;t have to inherit or mix in anything.
605
+
606
+ </li>
607
+ <li>By using only the file system and standard ruby libraries, installation
608
+ requirements are minimal.
609
+
610
+ </li>
611
+ <li>It may be fast enough for many purposes, especially using multiple
612
+ processes rather than multiple threads.
613
+
614
+ </li>
615
+ <li>Pure ruby. Ruby license. Free sotware.
616
+
617
+ </li>
618
+ </ul>
619
+ <h2>Applications</h2>
620
+ <p>
621
+ I&#8217;ve heard from a couple of people writing applications that use <a
622
+ href="../classes/FSDB.html">FSDB</a>. One app is:
623
+ </p>
624
+ <ul>
625
+ <li><a href="http://tourneybot.rubyforge.org">tourneybot.rubyforge.org</a>
626
+
627
+ </li>
628
+ </ul>
629
+ <h2>To do</h2>
630
+ <h3>Fix (potential) bugs</h3>
631
+ <ul>
632
+ <li>If two FSDBs are in use in the same process, they share the cache. If they
633
+ associate different formats with the same file, the results will be
634
+ surprising. Maybe the cache should remember the format used and flag an
635
+ error if it detects an inconsistency. A similar problem could happen for
636
+ other Database attributes, like lock-type (which should probably be
637
+ global).
638
+
639
+ </li>
640
+ </ul>
641
+ <h3>Features</h3>
642
+ <ul>
643
+ <li>Should the Format objects be classes instead of just instances of Format?
644
+
645
+ </li>
646
+ <li>Default value and proc for Database, like Hash.
647
+
648
+ </li>
649
+ <li>FSDB::Reference class:
650
+
651
+ <pre>
652
+ db['foo/bar.obj'] = &quot;some string&quot;
653
+ referrer = { :my_bar =&gt; FSDB::Reference.new('../foo/bar.obj') }
654
+ db['x/y.yml'] = referrer
655
+ p db['x/y.yml'][:my_bar] # ==&gt; &quot;some string&quot;
656
+ </pre>
657
+ <p>
658
+ Or, more like DRbUndumped:
659
+ </p>
660
+ <pre>
661
+ str = &quot;some string&quot;
662
+ str.extend FSDB::Undumped
663
+ db['foo/bar.obj'] = str
664
+ referrer = { :my_bar =&gt; str }
665
+ db['x/y.yml'] = referrer
666
+ p db['x/y.yml'][:my_bar] # ==&gt; &quot;some string&quot;
667
+ </pre>
668
+ <p>
669
+ Extending with FSDB::Undumped will have to insert state in the object that
670
+ remembers the db path at which it is stored (&#8216;foo/bar.obj&#8217; in
671
+ this case).
672
+ </p>
673
+ </li>
674
+ <li>Use (optionally) weak references in CacheEntry.
675
+
676
+ </li>
677
+ <li>use metafiles to emulate locking on dirs?
678
+
679
+ </li>
680
+ <li>optionally, for each file, store a md5 sum of the raw data, so that we may
681
+ be able to avoid Marshal.load and (after dump) the actual write.
682
+
683
+ </li>
684
+ <li>optionally, do not create ..fsdb.meta.* files.
685
+
686
+ </li>
687
+ <li>transactions on groups of objects
688
+
689
+ <ul>
690
+ <li>for edit and browse, but not replace or insert. Maybe delete.
691
+
692
+ </li>
693
+ <li>db.edit [path1, path2] do |obj1, obj2| &#8230; end
694
+
695
+ <ul>
696
+ <li>lock order is explicit, up to user to avoid deadlock
697
+
698
+ </li>
699
+ </ul>
700
+ </li>
701
+ <li>db.edit_glob &quot;foo/**/bar*/{zap,zow}&quot; &#8230; do |hash|
702
+
703
+ <pre>
704
+ for path, object in hash ... end
705
+ </pre>
706
+ <p>
707
+ end
708
+ </p>
709
+ </li>
710
+ </ul>
711
+ </li>
712
+ <li>Make irb-based database shell
713
+
714
+ <ul>
715
+ <li>class Database; def irb_browse(path); browse(path) {|obj| irb obj}; end;
716
+ end
717
+
718
+ <p>
719
+ then:
720
+ </p>
721
+ <pre>
722
+ irb&gt; irb db
723
+ irb#1&gt; irb_browse path
724
+ ...
725
+ ... # got a read lock for this session
726
+ ...
727
+ irb#1&gt; ^D
728
+ irb&gt;
729
+ </pre>
730
+ <p>
731
+ one problem: irb defines singleton methods, so can&#8217;t dump (in edit)
732
+ </p>
733
+ <p>
734
+ maybe we can extend the class of the object by some module instead&#8230;
735
+ </p>
736
+ </li>
737
+ </ul>
738
+ </li>
739
+ <li>iterator, query, indexing methods
740
+
741
+ </li>
742
+ <li>more formats
743
+
744
+ <ul>
745
+ <li>tabular data, excel, xml, ascii db, csv
746
+
747
+ </li>
748
+ <li>SOAP marshal, XML marshal
749
+
750
+ </li>
751
+ <li>filters for compression, encryption
752
+
753
+ </li>
754
+ </ul>
755
+ </li>
756
+ <li>more node types
757
+
758
+ <pre>
759
+ .que : use IO#read_object, IO#write_object (at end of file)
760
+ to implement a persistent queue
761
+
762
+ fifo, named socket, device, ...
763
+ </pre>
764
+ </li>
765
+ <li>interface to file attributes (mode, etc)
766
+
767
+ </li>
768
+ <li>access control lists (use meta files)
769
+
770
+ </li>
771
+ </ul>
772
+ <h3>Stability, Security, and Error Checking</h3>
773
+ <ul>
774
+ <li>investigate using the BDB lock mechanism in place of flock.
775
+
776
+ </li>
777
+ <li>in transactions, if path is tainted, apply the validation of util.rb?
778
+
779
+ </li>
780
+ <li>detect fork in transaction
781
+
782
+ </li>
783
+ <li>purge empty dirs?
784
+
785
+ </li>
786
+ <li>periodically clear_cache to keep the hash size low
787
+
788
+ <ul>
789
+ <li>every Nth new CacheEntry?
790
+
791
+ </li>
792
+ <li>should cache entries be in an LRU queue so we can purge the LRU?
793
+
794
+ </li>
795
+ </ul>
796
+ </li>
797
+ <li>should we detect recursive lock attempt and fail? (Now, it just deadlocks.)
798
+
799
+ </li>
800
+ </ul>
801
+ <h3>Performance</h3>
802
+ <ul>
803
+ <li>Profiling says that Thread.exclusive consumes about 20% of cpu. Also,
804
+ Thread.stop and Thread.run when there are multiple threads. Using
805
+ Thread.critical in places where it is safe to do so (no exceptions raised)
806
+ instead of Thread.exclusive would reduce this to an estimated 6%. ((See
807
+ faster-modex .rb and faster-mutex.rb.))
808
+
809
+ </li>
810
+ <li>Better way of waiting for file lock in the multithread case
811
+
812
+ <ul>
813
+ <li>this may be unfixable until ruby has native threads
814
+
815
+ </li>
816
+ </ul>
817
+ </li>
818
+ <li>Use shared memory for the cache, so write is not necessary after edit.
819
+
820
+ <ul>
821
+ <li>actually, this may not make much sense
822
+
823
+ </li>
824
+ </ul>
825
+ </li>
826
+ <li>Option for Database to ignore file locking and possibility of other
827
+ writers.
828
+
829
+ </li>
830
+ <li>fetch could use the cache better if the cache kept the file contents string
831
+ as well as the loaded object. Then the stale! call would only have to wipe
832
+ the reference to the object, and could leave the contents string. But this
833
+ would increase file size and duplicate the file system&#8217;s own cache.
834
+
835
+ </li>
836
+ </ul>
837
+ <h2>Version</h2>
838
+ <p>
839
+ fsdb 0.5
840
+ </p>
841
+ <p>
842
+ The current version of this software can be found at <a
843
+ href="http://redshift.sourceforge.net/fsdb">redshift.sourceforge.net/fsdb</a>.
844
+ </p>
845
+ <h2>License</h2>
846
+ <p>
847
+ This software is distributed under the Ruby license. See <a
848
+ href="http://www.ruby-lang.org">www.ruby-lang.org</a>.
849
+ </p>
850
+ <h2>Author</h2>
851
+ <p>
852
+ Joel VanderWerf, <a
853
+ href="mailto:vjoel@users.sourceforge.net">vjoel@users.sourceforge.net</a>
854
+ Copyright &#169; 2005, Joel VanderWerf.
855
+ </p>
856
+
857
+ </div>
858
+
859
+
860
+ </div>
861
+
862
+
863
+ </div>
864
+
865
+
866
+ <!-- if includes -->
867
+
868
+ <div id="section">
869
+
870
+
871
+
872
+
873
+
874
+
875
+
876
+
877
+ <!-- if method_list -->
878
+
879
+
880
+ </div>
881
+
882
+
883
+ <div id="validator-badges">
884
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
885
+ </div>
886
+
887
+ </body>
888
+ </html>