fsdb 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/{README.txt → README.markdown} +137 -126
- data/bench/bench.rb +1 -1
- data/examples/forks.rb +43 -0
- data/lib/fsdb/database.rb +5 -16
- data/lib/fsdb/file-lock.rb +15 -58
- data/lib/fsdb/modex.rb +39 -42
- data/test/test-concurrency.rb +2 -2
- data/test/test-concurrency/init.rb +0 -4
- data/test/test-formats.rb +1 -1
- data/test/test-fsdb.rb +3 -1
- data/test/test-modex.rb +64 -20
- data/test/test.rb +1 -7
- metadata +56 -98
- data/ext/fsdb/MANIFEST +0 -1
- data/ext/fsdb/extconf.rb +0 -9
- data/ext/fsdb/fcntl-lock.c +0 -112
- data/lib/fsdb/compat.rb +0 -42
- data/lib/fsdb/faster-modex.rb +0 -223
- data/lib/fsdb/faster-mutex.rb +0 -138
- data/lib/fsdb/mutex.rb +0 -137
- data/rakefile +0 -41
- data/tasks/ann.rake +0 -80
- data/tasks/bones.rake +0 -20
- data/tasks/gem.rake +0 -201
- data/tasks/git.rake +0 -40
- data/tasks/notes.rake +0 -27
- data/tasks/post_load.rake +0 -34
- data/tasks/rdoc.rake +0 -51
- data/tasks/rubyforge.rake +0 -55
- data/tasks/setup.rb +0 -292
- data/tasks/spec.rake +0 -54
- data/tasks/svn.rake +0 -47
- data/tasks/test.rake +0 -40
- data/tasks/zentest.rake +0 -36
- data/test/err.txt +0 -31
- data/test/test-mutex.rb +0 -33
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
fsdb 0.7.0
|
2
|
+
|
3
|
+
- remove fcntl locking option; fsdb is pure ruby again.
|
4
|
+
|
5
|
+
- removed obsolete mutex classes; ruby 1.8.7 and 1.9.3 don't need them.
|
6
|
+
|
7
|
+
- fixed problems in Modex class under ruby 1.9.3.
|
8
|
+
|
9
|
+
- removed workaround code for obsolete ruby versions.
|
10
|
+
|
1
11
|
fsdb 0.6.1
|
2
12
|
|
3
13
|
- Improved gem packaging.
|
@@ -1,71 +1,58 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
# What is FSDB?
|
3
3
|
|
4
|
-
FSDB is a file system data base. FSDB provides a thread-safe, process-safe Database class which uses the native file system as its back end and allows multiple file formats and serialization methods. Users access objects in terms of their paths relative to the base directory of the database. It's very light weight (the state of a Database is essentially just a path string, and code size is very small, under 1K lines, all ruby).
|
4
|
+
FSDB is a file system data base. FSDB provides a thread-safe, process-safe Database class which uses the native file system as its back end and allows multiple file formats and serialization methods. Users access objects in terms of their paths relative to the base directory of the database. It's very light weight (the per-process state of a Database, excluding cached data, is essentially just a path string, and code size is very small, under 1K lines, all ruby).
|
5
5
|
|
6
|
-
FSDB stores
|
6
|
+
FSDB stores data at nodes in the file system. The format can vary depending on type. For example, the default file type can be read into your program as a string, but files with the .obj suffix could be read using marshal, and files with the .yaml suffix as yaml. FSDB can easily be extended to recognize other formats, both binary and text. FSDB treats directories as collections and provides directory iterator methods. Files are the atoms of transactions: each file is saved and restored as a whole. References between objects stored in different files can be persisted as path strings.
|
7
7
|
|
8
8
|
FSDB has been tested on a variety of platforms and ruby versions, and is not known to have any problems. (On WindowsME/98/95, multiple processes can access a database unsafely, because flock() is not available on the platform.) See the Testing section for details.
|
9
9
|
|
10
10
|
FSDB does not yet have any indexing or querying mechanisms, and is probably missing many other useful database features, so it is not a general replacement for RDBs or OODBs. However, if you are looking for a lightweight, concurrent object store with reasonable performance and better granularity than PStore, in pure Ruby, with a Ruby license, take a look at FSDB. Also, if you are looking for an easy way of making an existing file tree look like a database, especially if it has heterogeneous file formats, FSDB might be useful.
|
11
11
|
|
12
12
|
|
13
|
-
|
13
|
+
## Synopsis
|
14
14
|
|
15
|
-
|
15
|
+
```ruby
|
16
|
+
require 'fsdb'
|
16
17
|
|
17
|
-
|
18
|
+
db = FSDB::Database.new('/tmp/my-data')
|
18
19
|
|
19
|
-
|
20
|
+
db['recent-movies/myself'] = ["The King's Speech", "Harry Potter 7"]
|
21
|
+
puts db['recent-movies/myself'][1] # ==> "Harry Potter 7"
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
db.edit 'recent-movies/myself' do |movies|
|
24
|
+
movies << "The Muppets"
|
25
|
+
end
|
26
|
+
```
|
24
27
|
|
25
|
-
== Synopsis
|
26
|
-
|
27
|
-
require 'fsdb'
|
28
|
-
|
29
|
-
db = FSDB::Database.new('/tmp/my-data')
|
30
|
-
|
31
|
-
db['recent-movies/myself'] = ["LOTR II", "Austin Powers"]
|
32
|
-
puts db['recent-movies/myself'][0] # ==> "LOTR II"
|
33
|
-
|
34
|
-
db.edit 'recent-movies/myself' do |list|
|
35
|
-
list << "A la recherche du temps perdu"
|
36
|
-
end
|
37
28
|
|
38
|
-
|
39
|
-
== Path names
|
29
|
+
## Path names
|
40
30
|
|
41
31
|
Keys in the database are path strings, which are simply strings in the usual forward-slash delimited format, relative to the database's directory. There are some points to be aware of when using them to refer to database objects.
|
42
32
|
|
43
33
|
* Paths to directories are formed in one of two ways:
|
44
34
|
|
45
|
-
- explicitly, with a trailing slash, as in
|
35
|
+
- explicitly, with a trailing slash, as in `db['foo/']`
|
46
36
|
|
47
|
-
- implicitly, as in
|
48
|
-
in
|
37
|
+
- implicitly, as in `db['foo']` if `foo` is already a directory, or as
|
38
|
+
in `db['foo/bar']`, which creates `foo` if it did not
|
49
39
|
already exist.
|
50
40
|
|
51
|
-
|
52
|
-
of the form
|
41
|
+
* The root dir of the database is simply `/`, its child directories are
|
42
|
+
of the form `foo/` and so on. The leading and trailing slashes are
|
53
43
|
both optional.
|
54
44
|
|
55
45
|
* Objects can be stored in various formats, indicated by path name. A typical
|
56
46
|
mapping might be:
|
57
47
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
browse, edit, etc.)
|
48
|
+
file name | de-serialized data type
|
49
|
+
--------- | --------------
|
50
|
+
`foo.obj` | Marshalled data
|
51
|
+
`foo.txt` | String
|
52
|
+
`foo/` | Directory (the contents is presented to the caller as a list of file and subdirectory paths that can be used in browse, edit, etc.)
|
53
|
+
`foo.yml` | YAML data--see examples/yaml.rb
|
65
54
|
|
66
|
-
|
67
|
-
|
68
|
-
New formats, which correlate filename pattern with serialization behavior,
|
55
|
+
New formats, which correlate filename pattern with serialization behavior,
|
69
56
|
can be defined and plugged in to databases. Each format has its own rules for
|
70
57
|
matching patterns in the file name and recognizing the file. Patterns can be
|
71
58
|
anything with a #=== method (such as a regex). See lib/fsdb/formats.rb
|
@@ -74,43 +61,48 @@ Keys in the database are path strings, which are simply strings in the usual for
|
|
74
61
|
|
75
62
|
* Different notations for the same path, such as
|
76
63
|
|
64
|
+
```
|
77
65
|
/foo/bar
|
78
66
|
foo/bar
|
79
67
|
foo//bar
|
80
68
|
foo/../foo/bar
|
69
|
+
```
|
81
70
|
|
82
|
-
|
71
|
+
work correctly (they access the same objects), as do paths that denote hard
|
83
72
|
or soft links, if supported on the platform.
|
84
73
|
|
85
|
-
|
74
|
+
Links are subject to the same naming convention as normal files with regard
|
86
75
|
to format identification: format is determined by the path within the
|
87
76
|
database used to access the object. Using a different name for a link can
|
88
77
|
be useful if you need to access the file using two different formats (e.g.,
|
89
|
-
plain text via
|
78
|
+
plain text via `foo.txt` and tabular CSV or TSV data via `foo.table` or
|
79
|
+
whatever).
|
90
80
|
|
91
81
|
* Accessing objects in a database is unaffected by the current dir of your
|
92
82
|
process. The database knows it's own absolute path, and path arguments to
|
93
83
|
the Database API are interpreted relative to that. If you want to work with a
|
94
84
|
subdirectory of the database, and paths relative to that, use Database#subdb:
|
95
85
|
|
86
|
+
``` ruby
|
96
87
|
db = Database.new['/tmp']
|
97
88
|
db['foo/bar'] = 1
|
98
89
|
foo = db.subdb('foo')
|
99
90
|
foo['bar'] # ==> 1
|
91
|
+
```
|
100
92
|
|
101
|
-
* Paths that are outside the database (
|
93
|
+
* Paths that are outside the database (`../../zap`) are allowed, but may or may
|
102
94
|
not be desirable. Use #valid? and #validate in util.rb to check for them.
|
103
95
|
|
104
|
-
* Directories are created when needed. So db['a/b/c'] = 1 creates two dirs and
|
96
|
+
* Directories are created when needed. So `db['a/b/c'] = 1` creates two dirs and
|
105
97
|
one file.
|
106
98
|
|
107
|
-
* Files beginning with
|
99
|
+
* Files beginning with `..` are ignored by fsdb dir iterators, though they
|
108
100
|
can still be accessed in transaction operators. Some such files
|
109
|
-
(
|
110
|
-
beginning with
|
101
|
+
(`..fsdb.meta.<filename>`) are used internally. All others _not_
|
102
|
+
beginning with `..fsdb` are reserved for applications to use.
|
111
103
|
|
112
|
-
|
113
|
-
|
104
|
+
The `..fsdb.meta.<filename>` file holds a version number for
|
105
|
+
`<filename>`, which is used along with mtime to check for changes (mtime
|
114
106
|
usually has a precision of only 1 second). In the future, the file may also
|
115
107
|
be used to hold other metadata. (The meta file is only created when a file is
|
116
108
|
written to and does not need to be created in advance when using existing
|
@@ -119,7 +111,7 @@ Keys in the database are path strings, which are simply strings in the usual for
|
|
119
111
|
* util.rb has directory iterators, path globbing, and other useful tools.
|
120
112
|
|
121
113
|
|
122
|
-
|
114
|
+
## Transactions
|
123
115
|
|
124
116
|
FSDB transactions are thread-safe and process-safe. They can be nested for
|
125
117
|
larger-grained transactions; it is the user's responsibility to avoid deadlock.
|
@@ -128,38 +120,45 @@ FSDB is ACID (atomic/consistent/isolated/durable) to the extent that the underly
|
|
128
120
|
|
129
121
|
There are two kinds of transactions:
|
130
122
|
|
131
|
-
- A simple transfer of a value, as in
|
123
|
+
- A simple transfer of a value, as in `db['x']` and `db['x'] = 1`.
|
132
124
|
|
133
|
-
|
125
|
+
Note that a sequence of such transactions is not itself a transaction, and
|
134
126
|
can be affected by other processes and threads.
|
135
127
|
|
128
|
+
``` ruby
|
136
129
|
db['foo/bar'] = [1,2,3]
|
137
|
-
db['foo/bar'] += [4] # This is actually 2 transactions
|
130
|
+
db['foo/bar'] += [4] # This line is actually 2 transactions
|
138
131
|
db['foo/bar'][-1]
|
132
|
+
```
|
139
133
|
|
140
|
-
|
134
|
+
It is possible for the result of these transactions to be `4`. But, if
|
141
135
|
other threads or processes are scheduled during this code fragment, the
|
142
136
|
result could be a completely different value, or the code could raise an
|
143
|
-
|
144
|
-
with one that does not have the
|
145
|
-
The four operations are atomic by themselves, but the sequence is not.
|
137
|
+
method_missing exception because the object at the path has been replaced
|
138
|
+
with one that does not have the `+` method or the `[ ]` method.
|
139
|
+
The four operations are each atomic by themselves, but the sequence is not.
|
146
140
|
|
147
|
-
|
148
|
-
be made using destructive methods (such as
|
149
|
-
assignments of the form
|
141
|
+
Note that changes to a database object using this kind of transaction cannot
|
142
|
+
be made using destructive methods (such as `<<`) but only by
|
143
|
+
assignments of the form `db[<path>] = <data>`. Note that `+=`
|
150
144
|
and similar "assignment operators" can be used but are not atomic, because
|
151
145
|
|
146
|
+
``` ruby
|
152
147
|
db[<path>] += 1
|
148
|
+
```
|
149
|
+
|
150
|
+
is really
|
153
151
|
|
154
|
-
|
155
|
-
|
152
|
+
``` ruby
|
156
153
|
db[<path>] = db[<path>] + 1
|
154
|
+
```
|
157
155
|
|
158
|
-
|
156
|
+
So another thread or process could change the value stored at `path` while
|
159
157
|
the addition is happening.
|
160
158
|
|
161
159
|
- Transactions that allow more complex interaction:
|
162
160
|
|
161
|
+
``` ruby
|
163
162
|
path = 'foo/bar'
|
164
163
|
db[path] = [1,2,3]
|
165
164
|
|
@@ -167,14 +166,15 @@ There are two kinds of transactions:
|
|
167
166
|
bar += [4]
|
168
167
|
bar[-1]
|
169
168
|
end
|
169
|
+
```
|
170
170
|
|
171
|
-
|
171
|
+
This guarantees that, if the object at the path is still `[1, 2, 3]`
|
172
172
|
at the time of the #edit call, the value returned by the transaction will be
|
173
173
|
4.
|
174
174
|
|
175
|
-
|
176
|
-
the duration of the block. Other threads or processes that use FSDB
|
177
|
-
to read or write the object will be blocked for the duration of the
|
175
|
+
Simply put, #edit allows exclusive write access to the object at the path
|
176
|
+
for the duration of the block. Other threads or processes that use FSDB
|
177
|
+
methods to read or write the object will be blocked for the duration of the
|
178
178
|
transaction. There is also #browse, which allows read access shared by any
|
179
179
|
number of threads and processes, and #replace, which also allows exclusive
|
180
180
|
write access like #edit. The differences between #replace and #edit are:
|
@@ -184,29 +184,30 @@ There are two kinds of transactions:
|
|
184
184
|
(The new value in #replace's block can be a modification of the old value,
|
185
185
|
or an entirely different object.)
|
186
186
|
|
187
|
-
- #replace yields
|
187
|
+
- #replace yields `nil` if there is no preexisting object, whereas #edit
|
188
188
|
calls #default_edit (which by default calls #object_missing, which by
|
189
189
|
default throws MissingObjectError).
|
190
190
|
|
191
191
|
- #edit is useless over a drb connection, since is it operating on a
|
192
192
|
Marshal.dump-ed copy. Use #replace with drb.
|
193
193
|
|
194
|
-
|
195
|
-
#delete, which returns the object. Also, #delete can take a block,
|
196
|
-
examine the object and abort the transaction to prevent deletion.
|
197
|
-
delete transaction has the same exclusion semantics as
|
194
|
+
You can delete an object from the database (and the file system) with the
|
195
|
+
#delete method, which returns the object. Also, #delete can take a block,
|
196
|
+
which can examine the object and abort the transaction to prevent deletion.
|
197
|
+
(The delete transaction has the same exclusion semantics as edit and
|
198
|
+
replace.)
|
198
199
|
|
199
|
-
|
200
|
-
|
200
|
+
The #fetch and #insert methods are aliased with `[ ]` and
|
201
|
+
`[ ]=`.
|
201
202
|
|
202
|
-
|
203
|
+
When the object at the path specified in a transaction does not exist in the
|
203
204
|
file system, the different transaction methods behave differently:
|
204
205
|
|
205
206
|
- #browse calls #default_browse, which, in Database's implementation, calls
|
206
|
-
|
207
|
+
object_missing, which raises Database::MissingObjectError.
|
207
208
|
|
208
209
|
- #edit calls #default_edit, which, in Database's implementation, calls
|
209
|
-
|
210
|
+
object_missing, which raises Database::MissingObjectError.
|
210
211
|
|
211
212
|
- #replace and #insert (and #[]) ignore any missing file.
|
212
213
|
|
@@ -216,28 +217,30 @@ There are two kinds of transactions:
|
|
216
217
|
- #fetch calls #default_fetch, which, in Database's implementation, returns
|
217
218
|
nil.
|
218
219
|
|
219
|
-
|
220
|
+
Transactions can be nested. However, the order in which objects are locked
|
220
221
|
can lead to deadlock if, for example, the nesting is cyclic, or two threads
|
221
222
|
or processes request the same set of locks in a different order. One approach
|
222
223
|
is to only request nested locks on paths in the lexicographic order of the
|
223
224
|
path strings: "foo/bar", "foo/baz", ...
|
224
225
|
|
225
|
-
|
226
|
+
A transaction can be aborted with Database#abort and Database.abort, after
|
226
227
|
which the state of the object in the database remains as before the
|
227
228
|
transaction. An exception that is raised but not handled within a
|
228
229
|
transaction also aborts the transaction.
|
229
230
|
|
230
|
-
|
231
|
+
Note that there is no locking on directories, but you can designate a lock
|
231
232
|
file for each dir and effectively have multiple-reader, single writer
|
232
233
|
(advisory) locking on dirs. Just make sure you enclose your dir operation
|
233
234
|
in a transaction on the lock object, and always access these objects using
|
234
235
|
this technique.
|
235
236
|
|
237
|
+
``` ruby
|
236
238
|
db.browse('lock for dir') do
|
237
239
|
db['dir/x'] = 1
|
238
240
|
end
|
241
|
+
```
|
239
242
|
|
240
|
-
|
243
|
+
## Guarding against concurrency problems
|
241
244
|
|
242
245
|
- It's the user's responsibility to avoid deadlock. See above.
|
243
246
|
|
@@ -247,20 +250,16 @@ There are two kinds of transactions:
|
|
247
250
|
|
248
251
|
- It is not safe to fork while in a transaction.
|
249
252
|
|
250
|
-
|
251
|
-
flock call. This is necessary for Linux NFS, for example. There doesn't seem
|
252
|
-
to be any performance difference when running on a local filesystem.
|
253
|
-
|
254
|
-
== Limitations
|
253
|
+
## Limitations
|
255
254
|
|
256
255
|
- Transactions are not journaled. There's no commit, undo, or versioning. (You
|
257
256
|
can abort a transaction, however.) These could be added...
|
258
257
|
|
259
|
-
|
258
|
+
## Testing
|
260
259
|
|
261
260
|
FSDB has been tested on the following platforms and file systems:
|
262
261
|
|
263
|
-
- Linux/x86 (single and dual cpu,
|
262
|
+
- Linux/x86 (single and dual cpu, ext3, ext4, and reiser file systems)
|
264
263
|
|
265
264
|
- Solaris/sparc (dual and quad cpu, nfs and ufs)
|
266
265
|
|
@@ -270,7 +269,7 @@ FSDB has been tested on the following platforms and file systems:
|
|
270
269
|
|
271
270
|
- Windows ME (single cpu, FAT32)
|
272
271
|
|
273
|
-
FSDB is currently tested with ruby-1.9.
|
272
|
+
FSDB is currently tested with ruby-1.9.3 and ruby-1.8.7.
|
274
273
|
|
275
274
|
On windows, both the mswin32 and mingw32 builds of ruby have been used with FSDB. It has never been tested with cygwin or bccwin.
|
276
275
|
|
@@ -278,7 +277,7 @@ The tests include unit and stress tests. Unit tests isolate individual features
|
|
278
277
|
|
279
278
|
The only known testing failure is on Windows ME (and presumably 95 and 98). The stress test succeeds with one process and multiple threads. It succeeds with multiple processes each with one thread. However, with two processes each with two threads, the test usually deadlocks very quickly.
|
280
279
|
|
281
|
-
|
280
|
+
## Performance
|
282
281
|
|
283
282
|
FSDB is not very fast. It's useful more for its safety, flexibility, and ease of use.
|
284
283
|
|
@@ -295,16 +294,16 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
|
|
295
294
|
- On an 850MHz PIII under linux, with debugging turned off (-b option),
|
296
295
|
test-concurrency.rb reports:
|
297
296
|
|
298
|
-
processes
|
299
|
-
|
300
|
-
1
|
301
|
-
1
|
302
|
-
10
|
303
|
-
10
|
304
|
-
10
|
305
|
-
10
|
297
|
+
processes | threads | objects | transactions per cpu second
|
298
|
+
--------- | --------- | --------- | ---------------------------
|
299
|
+
1 | 1 | 10 | 965
|
300
|
+
1 | 10 | 10 | 165
|
301
|
+
10 | 1 | 10 | 684
|
302
|
+
10 | 10 | 10 | 122
|
303
|
+
10 | 10 | 100 | 100
|
304
|
+
10 | 10 | 10000 | 92
|
306
305
|
|
307
|
-
|
306
|
+
These results are not representative of typical applications, because the
|
308
307
|
test was designed to stress the database and expose stability problems, not
|
309
308
|
to immitate typical use of database-stored objects. See bench/bench.rb for
|
310
309
|
for bechmarks.
|
@@ -314,23 +313,23 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
|
|
314
313
|
clear out the cache's reference to the object so that it will be loaded
|
315
314
|
freshly the next time #fetch is called on the path.
|
316
315
|
|
317
|
-
|
316
|
+
The performance hit of #fetch is of course greater with larger objects,
|
318
317
|
and with objects that are loaded by a more complex procedure, such as
|
319
318
|
Marshal.load.
|
320
319
|
|
321
|
-
|
322
|
-
you get different copies that do not share any parts. Or you can think
|
323
|
-
as File.read--it gives you an instantaneous snapshot of the file, but
|
324
|
-
not give you a transaction "window" in which no other thread or process
|
325
|
-
modify the object.
|
320
|
+
You can think of #fetch as a "deep copy" of the object. If you call it
|
321
|
+
twice, you get different copies that do not share any parts. Or you can think
|
322
|
+
of it as File.read--it gives you an instantaneous snapshot of the file, but
|
323
|
+
does not give you a transaction "window" in which no other thread or process
|
324
|
+
can modify the object.
|
326
325
|
|
327
|
-
|
326
|
+
There is no analogous concern with #insert and its alias #[]=. These methods
|
328
327
|
always write to the file system, but they also leave the object in the cache.
|
329
328
|
|
330
329
|
- Performance is worse on Windows. Most of the delay seems to be in system,
|
331
330
|
rather than user, code.
|
332
331
|
|
333
|
-
|
332
|
+
## Advantages
|
334
333
|
|
335
334
|
- FSDB is useful with heterogeneous data, that is, with files in varying
|
336
335
|
formats that can be recognized based on file name.
|
@@ -339,13 +338,15 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
|
|
339
338
|
types. By defining new format clases, it's easy to set up databases that
|
340
339
|
allow:
|
341
340
|
|
341
|
+
``` ruby
|
342
342
|
home['.forward'] += ["nobody@nowhere.net"]
|
343
343
|
etc.edit('passwd') { |passwd| passwd['fred'].shell = '/bin/zsh' }
|
344
344
|
window.setIcon(icons['apps/editor.png'])
|
345
|
-
|
345
|
+
```
|
346
|
+
|
346
347
|
- A FSDB can be operated on with ordinary file tools. FSDB can even treat
|
347
348
|
existing file hierarchies as databases. It's easy to backup, export, grep,
|
348
|
-
... the database. Its just files.
|
349
|
+
rsync, tar, ... the database. Its just files.
|
349
350
|
|
350
351
|
- FSDB is process-safe, so it can be used for *persistent*, *fault-tolerant*
|
351
352
|
interprocess communication, such as a queue that doesn't require both
|
@@ -382,7 +383,7 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
|
|
382
383
|
- Pure ruby. Ruby license. Free software.
|
383
384
|
|
384
385
|
|
385
|
-
|
386
|
+
## Applications
|
386
387
|
|
387
388
|
I've heard from a couple of people writing applications that use FSDB. One app
|
388
389
|
is:
|
@@ -390,9 +391,9 @@ is:
|
|
390
391
|
- http://tourneybot.rubyforge.org
|
391
392
|
|
392
393
|
|
393
|
-
|
394
|
+
## To do
|
394
395
|
|
395
|
-
|
396
|
+
### Fix (potential) bugs
|
396
397
|
|
397
398
|
- If two FSDBs are in use in the same process, they share the cache. If they
|
398
399
|
associate different formats with the same file, the results will be
|
@@ -401,7 +402,7 @@ is:
|
|
401
402
|
Database attributes, like lock-type (which should probably be global).
|
402
403
|
|
403
404
|
|
404
|
-
|
405
|
+
### Features
|
405
406
|
|
406
407
|
- Should the Format objects be classes instead of just instances of Format?
|
407
408
|
|
@@ -409,21 +410,25 @@ is:
|
|
409
410
|
|
410
411
|
- FSDB::Reference class:
|
411
412
|
|
413
|
+
``` ruby
|
412
414
|
db['foo/bar.obj'] = "some string"
|
413
415
|
referrer = { :my_bar => FSDB::Reference.new('../foo/bar.obj') }
|
414
416
|
db['x/y.yml'] = referrer
|
415
417
|
p db['x/y.yml'][:my_bar] # ==> "some string"
|
418
|
+
```
|
416
419
|
|
417
|
-
|
420
|
+
Or, more like DRbUndumped:
|
418
421
|
|
422
|
+
``` ruby
|
419
423
|
str = "some string"
|
420
424
|
str.extend FSDB::Undumped
|
421
425
|
db['foo/bar.obj'] = str
|
422
426
|
referrer = { :my_bar => str }
|
423
427
|
db['x/y.yml'] = referrer
|
424
428
|
p db['x/y.yml'][:my_bar] # ==> "some string"
|
429
|
+
```
|
425
430
|
|
426
|
-
|
431
|
+
Extending with FSDB::Undumped will have to insert state in the object that
|
427
432
|
remembers the db path at which it is stored ('foo/bar.obj' in this case).
|
428
433
|
|
429
434
|
- Use (optionally) weak references in CacheEntry.
|
@@ -439,20 +444,25 @@ is:
|
|
439
444
|
|
440
445
|
- for edit and browse, but not replace or insert. Maybe delete.
|
441
446
|
|
442
|
-
- db.edit [path1, path2] do |obj1, obj2| ... end
|
447
|
+
- `db.edit [path1, path2] do |obj1, obj2| ... end`
|
443
448
|
|
444
449
|
- lock order is explicit, up to user to avoid deadlock
|
445
450
|
|
446
|
-
-
|
451
|
+
- and:
|
452
|
+
|
453
|
+
``` ruby
|
454
|
+
db.edit_glob "foo/**/bar*/{zap,zow}" ... do |hash|
|
447
455
|
for path, object in hash ... end
|
448
456
|
end
|
457
|
+
```
|
449
458
|
|
450
459
|
- Make irb-based database shell
|
451
460
|
|
452
|
-
- class Database; def irb_browse(path); browse(path) {|obj| irb obj}; end; end
|
461
|
+
- `class Database; def irb_browse(path); browse(path) {|obj| irb obj}; end; end`
|
453
462
|
|
454
463
|
then:
|
455
464
|
|
465
|
+
```
|
456
466
|
irb> irb db
|
457
467
|
irb#1> irb_browse path
|
458
468
|
...
|
@@ -460,6 +470,7 @@ is:
|
|
460
470
|
...
|
461
471
|
irb#1> ^D
|
462
472
|
irb>
|
473
|
+
```
|
463
474
|
|
464
475
|
one problem: irb defines singleton methods, so can't dump (in edit)
|
465
476
|
|
@@ -489,7 +500,7 @@ is:
|
|
489
500
|
- access control lists (use meta files)
|
490
501
|
|
491
502
|
|
492
|
-
|
503
|
+
### Stability, Security, and Error Checking
|
493
504
|
|
494
505
|
- investigate using the BDB lock mechanism in place of flock.
|
495
506
|
|
@@ -508,7 +519,7 @@ is:
|
|
508
519
|
- should we detect recursive lock attempt and fail? (Now, it just deadlocks.)
|
509
520
|
|
510
521
|
|
511
|
-
|
522
|
+
### Performance
|
512
523
|
|
513
524
|
- Profiling says that Thread.exclusive consumes about 20% of cpu. Also,
|
514
525
|
Thread.stop and Thread.run when there are multiple threads. Using
|
@@ -526,20 +537,20 @@ is:
|
|
526
537
|
|
527
538
|
- Option for Database to ignore file locking and possibility of other writers.
|
528
539
|
|
529
|
-
-
|
540
|
+
- fetch could use the cache better if the cache kept the file contents string
|
530
541
|
as well as the loaded object. Then the #stale! call would only have to
|
531
542
|
wipe the reference to the object, and could leave the contents string. But
|
532
543
|
this would increase file size and duplicate the file system's own cache.
|
533
544
|
|
534
|
-
|
545
|
+
## Web site
|
535
546
|
|
536
|
-
The current version of this software can be found at http://rubyforge.org/projects/fsdb.
|
547
|
+
The current version of this software can be found at http://rubyforge.org/projects/fsdb. The main git repo is at https://github.com/vjoel/fsdb.
|
537
548
|
|
538
|
-
|
549
|
+
## License
|
539
550
|
|
540
551
|
This software is distributed under the Ruby license. See http://www.ruby-lang.org.
|
541
552
|
|
542
|
-
|
553
|
+
## Author
|
543
554
|
|
544
555
|
Joel VanderWerf, mailto:vjoel@users.sourceforge.net
|
545
|
-
Copyright (c) 2003-
|
556
|
+
Copyright (c) 2003-2011, Joel VanderWerf.
|