fsdb 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/History.txt +6 -0
  2. data/README.txt +1 -1
  3. data/lib/fsdb/database.rb +1 -1
  4. data/rakefile +5 -3
  5. data/test/test-concurrency.rb +5 -3
  6. data/test/test-formats.rb +3 -1
  7. data/test/test-fsdb.rb +5 -3
  8. metadata +10 -148
  9. data/junk/OLDRakefile +0 -98
  10. data/junk/OLDRakefile2 +0 -55
  11. data/junk/check-cache.rb +0 -18
  12. data/junk/create-lock.rb +0 -25
  13. data/junk/doc/old-api/classes/FSDB.html +0 -139
  14. data/junk/doc/old-api/classes/FSDB/Database.html +0 -953
  15. data/junk/doc/old-api/classes/FSDB/Database.src/M000029.html +0 -16
  16. data/junk/doc/old-api/classes/FSDB/Database.src/M000030.html +0 -16
  17. data/junk/doc/old-api/classes/FSDB/Database.src/M000031.html +0 -16
  18. data/junk/doc/old-api/classes/FSDB/Database.src/M000032.html +0 -16
  19. data/junk/doc/old-api/classes/FSDB/Database.src/M000033.html +0 -33
  20. data/junk/doc/old-api/classes/FSDB/Database.src/M000034.html +0 -18
  21. data/junk/doc/old-api/classes/FSDB/Database.src/M000035.html +0 -22
  22. data/junk/doc/old-api/classes/FSDB/Database.src/M000036.html +0 -16
  23. data/junk/doc/old-api/classes/FSDB/Database.src/M000037.html +0 -22
  24. data/junk/doc/old-api/classes/FSDB/Database.src/M000038.html +0 -43
  25. data/junk/doc/old-api/classes/FSDB/Database.src/M000039.html +0 -25
  26. data/junk/doc/old-api/classes/FSDB/Database.src/M000040.html +0 -43
  27. data/junk/doc/old-api/classes/FSDB/Database.src/M000041.html +0 -23
  28. data/junk/doc/old-api/classes/FSDB/Database.src/M000042.html +0 -22
  29. data/junk/doc/old-api/classes/FSDB/Database.src/M000043.html +0 -16
  30. data/junk/doc/old-api/classes/FSDB/Database.src/M000044.html +0 -16
  31. data/junk/doc/old-api/classes/FSDB/Database.src/M000045.html +0 -18
  32. data/junk/doc/old-api/classes/FSDB/Database.src/M000046.html +0 -18
  33. data/junk/doc/old-api/classes/FSDB/Database.src/M000047.html +0 -18
  34. data/junk/doc/old-api/classes/FSDB/Database.src/M000048.html +0 -16
  35. data/junk/doc/old-api/classes/FSDB/Database.src/M000049.html +0 -71
  36. data/junk/doc/old-api/classes/FSDB/Database.src/M000050.html +0 -43
  37. data/junk/doc/old-api/classes/FSDB/Database.src/M000051.html +0 -53
  38. data/junk/doc/old-api/classes/FSDB/Database.src/M000052.html +0 -44
  39. data/junk/doc/old-api/classes/FSDB/Database.src/M000053.html +0 -39
  40. data/junk/doc/old-api/classes/FSDB/Database.src/M000054.html +0 -72
  41. data/junk/doc/old-api/classes/FSDB/Database.src/M000055.html +0 -39
  42. data/junk/doc/old-api/classes/FSDB/Database.src/M000056.html +0 -18
  43. data/junk/doc/old-api/classes/FSDB/Database.src/M000057.html +0 -18
  44. data/junk/doc/old-api/classes/FSDB/Database.src/M000058.html +0 -18
  45. data/junk/doc/old-api/classes/FSDB/Database.src/M000059.html +0 -18
  46. data/junk/doc/old-api/classes/FSDB/Database.src/M000060.html +0 -18
  47. data/junk/doc/old-api/classes/FSDB/Database.src/M000061.html +0 -23
  48. data/junk/doc/old-api/classes/FSDB/Database.src/M000062.html +0 -23
  49. data/junk/doc/old-api/classes/FSDB/Database.src/M000063.html +0 -18
  50. data/junk/doc/old-api/classes/FSDB/Database.src/M000064.html +0 -18
  51. data/junk/doc/old-api/classes/FSDB/Database/AbortedTransaction.html +0 -118
  52. data/junk/doc/old-api/classes/FSDB/Database/CreateFileError.html +0 -120
  53. data/junk/doc/old-api/classes/FSDB/Database/DirIsImmutableError.html +0 -117
  54. data/junk/doc/old-api/classes/FSDB/Database/DirNotEmptyError.html +0 -117
  55. data/junk/doc/old-api/classes/FSDB/Database/FormatError.html +0 -117
  56. data/junk/doc/old-api/classes/FSDB/Database/MissingFileError.html +0 -117
  57. data/junk/doc/old-api/classes/FSDB/Database/MissingObjectError.html +0 -117
  58. data/junk/doc/old-api/classes/FSDB/Database/NotDirError.html +0 -118
  59. data/junk/doc/old-api/classes/FSDB/Database/PathComponentError.html +0 -120
  60. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.html +0 -148
  61. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000005.html +0 -21
  62. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000007.html +0 -21
  63. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.html +0 -210
  64. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000006.html +0 -22
  65. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000007.html +0 -22
  66. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000008.html +0 -22
  67. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000009.html +0 -22
  68. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000010.html +0 -22
  69. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000011.html +0 -22
  70. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000012.html +0 -22
  71. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000013.html +0 -22
  72. data/junk/doc/old-api/classes/FSDB/ForkSafely.html +0 -126
  73. data/junk/doc/old-api/classes/FSDB/Modex.html +0 -237
  74. data/junk/doc/old-api/classes/FSDB/Modex.src/M000024.html +0 -21
  75. data/junk/doc/old-api/classes/FSDB/Modex.src/M000025.html +0 -30
  76. data/junk/doc/old-api/classes/FSDB/Modex.src/M000026.html +0 -21
  77. data/junk/doc/old-api/classes/FSDB/Modex.src/M000027.html +0 -30
  78. data/junk/doc/old-api/classes/FSDB/Modex.src/M000028.html +0 -44
  79. data/junk/doc/old-api/classes/FSDB/Modex.src/M000029.html +0 -26
  80. data/junk/doc/old-api/classes/FSDB/Modex.src/M000030.html +0 -48
  81. data/junk/doc/old-api/classes/FSDB/Modex/ForkSafely.html +0 -105
  82. data/junk/doc/old-api/classes/FSDB/Mutex.html +0 -244
  83. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000018.html +0 -19
  84. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000019.html +0 -18
  85. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000020.html +0 -19
  86. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000021.html +0 -18
  87. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000022.html +0 -23
  88. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000023.html +0 -30
  89. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000024.html +0 -26
  90. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000025.html +0 -21
  91. data/junk/doc/old-api/classes/FSDB/Mutex/ForkSafely.html +0 -105
  92. data/junk/doc/old-api/classes/FSDB/PathUtilities.html +0 -257
  93. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000012.html +0 -23
  94. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000013.html +0 -18
  95. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000014.html +0 -23
  96. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000015.html +0 -18
  97. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000016.html +0 -18
  98. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000017.html +0 -22
  99. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000018.html +0 -23
  100. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000019.html +0 -18
  101. data/junk/doc/old-api/classes/FSDB/PathUtilities/InvalidPathError.html +0 -111
  102. data/junk/doc/old-api/classes/File.html +0 -272
  103. data/junk/doc/old-api/classes/File.src/M000001.html +0 -17
  104. data/junk/doc/old-api/classes/File.src/M000002.html +0 -17
  105. data/junk/doc/old-api/classes/File.src/M000003.html +0 -20
  106. data/junk/doc/old-api/classes/File.src/M000004.html +0 -20
  107. data/junk/doc/old-api/classes/File.src/M000005.html +0 -32
  108. data/junk/doc/old-api/classes/File.src/M000006.html +0 -32
  109. data/junk/doc/old-api/created.rid +0 -1
  110. data/junk/doc/old-api/files/README.html +0 -112
  111. data/junk/doc/old-api/files/RELEASE-NOTES.html +0 -233
  112. data/junk/doc/old-api/files/fsdb_txt.html +0 -888
  113. data/junk/doc/old-api/files/lib/fsdb/database_rb.html +0 -115
  114. data/junk/doc/old-api/files/lib/fsdb/file-lock_rb.html +0 -109
  115. data/junk/doc/old-api/files/lib/fsdb/modex_rb.html +0 -121
  116. data/junk/doc/old-api/files/lib/fsdb/mutex_rb.html +0 -108
  117. data/junk/doc/old-api/files/lib/fsdb/util_rb.html +0 -108
  118. data/junk/doc/old-api/fr_class_index.html +0 -47
  119. data/junk/doc/old-api/fr_file_index.html +0 -34
  120. data/junk/doc/old-api/fr_method_index.html +0 -90
  121. data/junk/doc/old-api/index.html +0 -24
  122. data/junk/doc/old-api/rdoc-style.css +0 -208
  123. data/junk/file-lock-nb.rb +0 -15
  124. data/junk/fl.rb +0 -144
  125. data/junk/flock-test.rb +0 -39
  126. data/junk/fsdb.kateproject +0 -47
  127. data/junk/fsdb.prj +0 -196
  128. data/junk/fsdb.sf +0 -46
  129. data/junk/insert-dir.rb +0 -48
  130. data/junk/lock-test-bug.rb +0 -150
  131. data/junk/lock-test-too-simple.rb +0 -136
  132. data/junk/lock-test.rb +0 -151
  133. data/junk/mkrdoc +0 -7
  134. data/junk/restore-fsdb.rb +0 -37
  135. data/junk/rf.txt +0 -5
  136. data/junk/solaris-bug-fixed.rb +0 -184
  137. data/junk/solaris-bug.rb +0 -182
  138. data/junk/solaris-bug.txt +0 -43
  139. data/junk/sync.rb +0 -327
  140. data/junk/test-file-lock.html +0 -86
  141. data/junk/test-file-lock.rb +0 -84
  142. data/junk/test-processes.rb +0 -131
  143. data/junk/test-threads.rb +0 -113
  144. data/junk/wiki-mutex.rb +0 -108
  145. data/misc/fsdb-blorubu.txt +0 -47
  146. data/misc/mtime-and-file-id.txt +0 -23
  147. data/misc/posixlock.txt +0 -148
@@ -1,888 +0,0 @@
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>