fsdb 0.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/{RELEASE-NOTES → History.txt} +6 -0
- data/{README → README.txt} +26 -17
- data/examples/flat.rb +146 -0
- data/examples/fsdb-example.rb +28 -0
- data/examples/rbformat.rb +17 -0
- data/examples/yaml2.rb +29 -0
- data/junk/OLDRakefile +98 -0
- data/junk/OLDRakefile2 +55 -0
- data/junk/check-cache.rb +18 -0
- data/junk/create-lock.rb +25 -0
- data/junk/doc/old-api/classes/FSDB.html +139 -0
- data/junk/doc/old-api/classes/FSDB/Database.html +953 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000029.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000030.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000031.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000032.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000033.html +33 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000034.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000035.html +22 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000036.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000037.html +22 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000038.html +43 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000039.html +25 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000040.html +43 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000041.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000042.html +22 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000043.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000044.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000045.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000046.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000047.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000048.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000049.html +71 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000050.html +43 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000051.html +53 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000052.html +44 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000053.html +39 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000054.html +72 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000055.html +39 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000056.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000057.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000058.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000059.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000060.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000061.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000062.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000063.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000064.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database/AbortedTransaction.html +118 -0
- data/junk/doc/old-api/classes/FSDB/Database/CreateFileError.html +120 -0
- data/junk/doc/old-api/classes/FSDB/Database/DirIsImmutableError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/DirNotEmptyError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/FormatError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/MissingFileError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/MissingObjectError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/NotDirError.html +118 -0
- data/junk/doc/old-api/classes/FSDB/Database/PathComponentError.html +120 -0
- data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.html +148 -0
- data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000005.html +21 -0
- data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000007.html +21 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.html +210 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000006.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000007.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000008.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000009.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000010.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000011.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000012.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000013.html +22 -0
- data/junk/doc/old-api/classes/FSDB/ForkSafely.html +126 -0
- data/junk/doc/old-api/classes/FSDB/Modex.html +237 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000024.html +21 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000025.html +30 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000026.html +21 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000027.html +30 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000028.html +44 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000029.html +26 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000030.html +48 -0
- data/junk/doc/old-api/classes/FSDB/Modex/ForkSafely.html +105 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.html +244 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000018.html +19 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000019.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000020.html +19 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000021.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000022.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000023.html +30 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000024.html +26 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000025.html +21 -0
- data/junk/doc/old-api/classes/FSDB/Mutex/ForkSafely.html +105 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.html +257 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000012.html +23 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000013.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000014.html +23 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000015.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000016.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000017.html +22 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000018.html +23 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000019.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities/InvalidPathError.html +111 -0
- data/junk/doc/old-api/classes/File.html +272 -0
- data/junk/doc/old-api/classes/File.src/M000001.html +17 -0
- data/junk/doc/old-api/classes/File.src/M000002.html +17 -0
- data/junk/doc/old-api/classes/File.src/M000003.html +20 -0
- data/junk/doc/old-api/classes/File.src/M000004.html +20 -0
- data/junk/doc/old-api/classes/File.src/M000005.html +32 -0
- data/junk/doc/old-api/classes/File.src/M000006.html +32 -0
- data/junk/doc/old-api/created.rid +1 -0
- data/junk/doc/old-api/files/README.html +112 -0
- data/junk/doc/old-api/files/RELEASE-NOTES.html +233 -0
- data/junk/doc/old-api/files/fsdb_txt.html +888 -0
- data/junk/doc/old-api/files/lib/fsdb/database_rb.html +115 -0
- data/junk/doc/old-api/files/lib/fsdb/file-lock_rb.html +109 -0
- data/junk/doc/old-api/files/lib/fsdb/modex_rb.html +121 -0
- data/junk/doc/old-api/files/lib/fsdb/mutex_rb.html +108 -0
- data/junk/doc/old-api/files/lib/fsdb/util_rb.html +108 -0
- data/junk/doc/old-api/fr_class_index.html +47 -0
- data/junk/doc/old-api/fr_file_index.html +34 -0
- data/junk/doc/old-api/fr_method_index.html +90 -0
- data/junk/doc/old-api/index.html +24 -0
- data/junk/doc/old-api/rdoc-style.css +208 -0
- data/junk/file-lock-nb.rb +15 -0
- data/junk/fl.rb +144 -0
- data/junk/flock-test.rb +39 -0
- data/junk/fsdb.kateproject +47 -0
- data/junk/fsdb.prj +196 -0
- data/junk/fsdb.sf +46 -0
- data/junk/insert-dir.rb +48 -0
- data/junk/lock-test-bug.rb +150 -0
- data/junk/lock-test-too-simple.rb +136 -0
- data/junk/lock-test.rb +151 -0
- data/{script → junk}/mkrdoc +0 -0
- data/junk/restore-fsdb.rb +37 -0
- data/junk/rf.txt +5 -0
- data/junk/solaris-bug-fixed.rb +184 -0
- data/junk/solaris-bug.rb +182 -0
- data/junk/solaris-bug.txt +43 -0
- data/junk/sync.rb +327 -0
- data/junk/test-file-lock.html +86 -0
- data/junk/test-file-lock.rb +84 -0
- data/junk/test-processes.rb +131 -0
- data/junk/test-threads.rb +113 -0
- data/junk/wiki-mutex.rb +108 -0
- data/lib/fsdb/database.rb +5 -3
- data/lib/fsdb/delegatable.rb +21 -0
- data/lib/fsdb/faster-modex.rb +223 -0
- data/lib/fsdb/faster-mutex.rb +138 -0
- data/lib/fsdb/mutex.rb +4 -1
- data/lib/fsdb/persistent.rb +91 -0
- data/lib/fsdb/read-write-object.rb +36 -0
- data/lib/fsdb/server.rb +44 -0
- data/misc/fsdb-blorubu.txt +47 -0
- data/misc/mtime-and-file-id.txt +23 -0
- data/misc/posixlock.txt +148 -0
- data/rakefile +39 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/err.txt +31 -0
- data/test/runs.rb +8 -0
- data/test/test-file-lock.rb +78 -0
- data/test/test-util.rb +1 -0
- data/test/trap.rb +31 -0
- metadata +198 -35
- data/Manifest +0 -36
- data/Rakefile +0 -10
- 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’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'] = ["LOTR II", "Austin Powers"]
|
|
125
|
+
puts db['recent-movies/myself'][0] # ==> "LOTR II"
|
|
126
|
+
|
|
127
|
+
db.edit 'recent-movies/myself' do |list|
|
|
128
|
+
list << "A la recherche du temps perdu"
|
|
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’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[‘foo/’]</tt>
|
|
143
|
+
|
|
144
|
+
</li>
|
|
145
|
+
<li>implicitly, as in <tt>db[‘foo’]</tt> if foo is already a
|
|
146
|
+
directory, or as in <tt>db[‘foo/bar’]</tt>, which creates
|
|
147
|
+
<tt>‘foo’</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—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 ‘foo.txt’ and tabular data via
|
|
202
|
+
‘foo.table’ 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’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'] # ==> 1
|
|
216
|
+
</pre>
|
|
217
|
+
</li>
|
|
218
|
+
<li>Paths that are outside the database (’../../zap’) 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[‘a/b/c’] = 1 creates
|
|
224
|
+
two dirs and one file.
|
|
225
|
+
|
|
226
|
+
</li>
|
|
227
|
+
<li>Files beginning with ’..’ are ignored by fsdb dir iterators,
|
|
228
|
+
though they can still be accessed in transaction operators. Some such files
|
|
229
|
+
(<tt>..fsdb.meta.<filename></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.<filename></tt> file holds a version number for
|
|
235
|
+
<filename>, 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’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’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[‘x’]</tt> and
|
|
269
|
+
<tt>db[‘x’] = 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><<</tt>) but
|
|
292
|
+
only by assignments of the form <tt>db[<path>] = <data></tt>.
|
|
293
|
+
Note that <tt>+=</tt> and similar "assignment operators" can be
|
|
294
|
+
used but are not atomic, because
|
|
295
|
+
</p>
|
|
296
|
+
<pre>
|
|
297
|
+
db[<path>] += 1
|
|
298
|
+
</pre>
|
|
299
|
+
<p>
|
|
300
|
+
is really
|
|
301
|
+
</p>
|
|
302
|
+
<pre>
|
|
303
|
+
db[<path>] = db[<path>] + 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’s block must return the new value, whereas edit’s block
|
|
337
|
+
must operate (destructively) on the block argument to produce the new
|
|
338
|
+
value. (The new value in replace’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’s implementation,
|
|
368
|
+
calls object_missing, which raises Database::MissingObjectError.
|
|
369
|
+
|
|
370
|
+
</li>
|
|
371
|
+
<li>edit calls default_edit, which, in Database’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’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: "foo/bar", "foo/baz",
|
|
393
|
+
…
|
|
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’s the user’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
|
+
"ghost" 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’s
|
|
431
|
+
usual flock call. This is necessary for Linux NFS, for example. There
|
|
432
|
+
doesn’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’s no commit, undo, or
|
|
440
|
+
versioning. (You can abort a transaction, however.) These could be
|
|
441
|
+
added…
|
|
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’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’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 "deep copy" 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—it gives you an instantaneous snapshot
|
|
535
|
+
of the file, but does not give you a transaction "window" 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’s easy to set up databases that allow:
|
|
559
|
+
|
|
560
|
+
<pre>
|
|
561
|
+
home['.forward'] += ["nobody@nowhere.net"]
|
|
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’s easy to backup, export, grep,
|
|
569
|
+
… 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’t require both processes to be alive at the same
|
|
575
|
+
time. It’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’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’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'] = "some string"
|
|
653
|
+
referrer = { :my_bar => FSDB::Reference.new('../foo/bar.obj') }
|
|
654
|
+
db['x/y.yml'] = referrer
|
|
655
|
+
p db['x/y.yml'][:my_bar] # ==> "some string"
|
|
656
|
+
</pre>
|
|
657
|
+
<p>
|
|
658
|
+
Or, more like DRbUndumped:
|
|
659
|
+
</p>
|
|
660
|
+
<pre>
|
|
661
|
+
str = "some string"
|
|
662
|
+
str.extend FSDB::Undumped
|
|
663
|
+
db['foo/bar.obj'] = str
|
|
664
|
+
referrer = { :my_bar => str }
|
|
665
|
+
db['x/y.yml'] = referrer
|
|
666
|
+
p db['x/y.yml'][:my_bar] # ==> "some string"
|
|
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 (‘foo/bar.obj’ 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| … 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 "foo/**/bar*/{zap,zow}" … 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> irb db
|
|
723
|
+
irb#1> irb_browse path
|
|
724
|
+
...
|
|
725
|
+
... # got a read lock for this session
|
|
726
|
+
...
|
|
727
|
+
irb#1> ^D
|
|
728
|
+
irb>
|
|
729
|
+
</pre>
|
|
730
|
+
<p>
|
|
731
|
+
one problem: irb defines singleton methods, so can’t dump (in edit)
|
|
732
|
+
</p>
|
|
733
|
+
<p>
|
|
734
|
+
maybe we can extend the class of the object by some module instead…
|
|
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’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 © 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>
|