fsdb 0.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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>