madeleine 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,31 @@
1
+
2
+ Copyright (c) 2003-2004, Anders Bengtsson
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ 3. The names of its contributors may not be used to endorse or promote
16
+ products derived from this software without specific prior written
17
+ permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
20
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
24
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
+ SUCH DAMAGE.
data/NEWS ADDED
@@ -0,0 +1,55 @@
1
+
2
+ Madeleine 0.7.1 (August 22, 2004):
3
+
4
+ * ZMarshal changed to work around Zlib bug.
5
+ * automatic_read_only fixed when intercepted class is inherited from
6
+
7
+ Madeleine 0.7 (July 23, 2004):
8
+
9
+ * Broken clock unit test on win32 fixed.
10
+ * AutomaticSnapshotMadeleine detects snapshot format on recovery
11
+ * Snapshot compression with Madeleine::ZMarshal
12
+ * YAML snapshots supported for automatic commands
13
+ * SOAP snapshots supported for automatic commands
14
+ * Read-only methods for automatic commands
15
+
16
+ Madeleine 0.6.1 (March 30, 2004):
17
+
18
+ * Bug fix: Use binary mode for I/O, fixes log replay
19
+ on mswin32 port of Ruby (Patch from Martin Tampe)
20
+
21
+ Madeleine 0.6 (March 28, 2004):
22
+
23
+ * Changed license to BSD
24
+ * Added a RubyGem specification
25
+ * Re-designed initialization (but still backward-compatible)
26
+ * Bug fix: Fixed use of finalized object's id in AutomaticSnapshotMadeleine
27
+
28
+ Madeleine 0.5 (August 31, 2003):
29
+
30
+ * Bug fix: Log order on recovery was wrong on some platforms
31
+ (Reported by IIMA Susumu)
32
+ * No longer requires the system clock to always increase
33
+ * Shared locks for queries
34
+
35
+ Madeleine 0.4 (July 4, 2003):
36
+
37
+ * Deprecated ClockedSnapshotMadeleine
38
+ * Added execute_query()
39
+ * API documentation in RDoc format
40
+
41
+ Madeleine 0.3 (May 15, 2003):
42
+
43
+ * Automatic commands
44
+ * Some classes exported to the default module
45
+ * Clock support not loaded by default (require 'madeleine/clock')
46
+ * Bug fix: Error handling when replaying logged commands.
47
+ * New system through block instead of argument (API change)
48
+ * Works in $SAFE = 1
49
+
50
+ Madeleine 0.2:
51
+
52
+ * Supports custom marshalling implementations.
53
+ * Changed interface for ClockedSystem and Clock.
54
+ * Some documentation added, including API docs.
55
+
data/README ADDED
@@ -0,0 +1,78 @@
1
+
2
+ Madeleine is a Ruby implementation of Object Prevalence: Transparent
3
+ persistence of business objects using command logging and complete
4
+ system snapshots.
5
+
6
+ <http://madeleine.sourceforge.net/>
7
+
8
+ Madeleine's design is based on Prevayler, the original Java
9
+ prevalence layer.
10
+
11
+ Learn more about object prevalence at <http://www.prevayler.org/>.
12
+
13
+
14
+ Installation:
15
+
16
+ Typical installation procedure is:
17
+ $ ruby install.rb config
18
+ $ ruby install.rb setup
19
+ # ruby install.rb install (may require root privilege)
20
+ Try 'ruby install.rb --help' for detailed usage.
21
+
22
+ [From the documentation of Minero Aoki's 'install.rb']
23
+
24
+ Usage:
25
+
26
+ require 'madeleine'
27
+
28
+ # Create an application as a prevalent system
29
+
30
+ madeleine = SnapshotMadeleine.new("my_example_storage") {
31
+ SomeExampleApplication.new()
32
+ }
33
+
34
+ # Do modifications of the system by sending commands through
35
+ # the Madeleine instance. A command is an object with a suitable
36
+ # "execute(system)" method.
37
+
38
+ madeleine.execute_command(command)
39
+
40
+
41
+ Requirements:
42
+
43
+ * Ruby 1.8.1 or later
44
+
45
+ Additionaly, some of the sample code also uses ruby/tk.
46
+
47
+
48
+ Known problems:
49
+
50
+ * Won't run in some Windows-ports of Ruby due to missing
51
+ fsync() call.
52
+
53
+ Contact:
54
+
55
+ Homepage:
56
+ <http://madeleine.sourceforge.net/>
57
+
58
+ Questions, bug reports, patches, complaints? Use the mailing list:
59
+ <http://lists.sourceforge.net/lists/listinfo/madeleine-devel>
60
+
61
+ License:
62
+
63
+ BSD (see the file COPYING)
64
+
65
+ Credits:
66
+
67
+ Anders Bengtsson - Prevalence core impl.
68
+ Stephen Sykes - Automatic commands impl.
69
+
70
+ With the help of patches, testing and feedback from:
71
+
72
+ Steve Conover, David Heinemeier Hansson, Johan Lind, H�kan R�berg,
73
+ IIMA Susumu, Martin Tampe and Jon Tirs�n
74
+
75
+ Thanks to Klaus Wuestefeld and the Prevayler developers for the
76
+ model of this software; to Minero Aoki for the installer; to Matz and
77
+ the core developers for the Ruby language!
78
+
@@ -0,0 +1,298 @@
1
+ # Batched writes for Madeleine
2
+ #
3
+ # Copyright(c) H�kan R�berg 2003
4
+ #
5
+ #
6
+ # This is an experimental implementation of batched log writes to mininize
7
+ # calls to fsync. It uses a Shared/Exclusive-Lock, implemented in sync.rb,
8
+ # which is included in Ruby 1.8.
9
+ #
10
+ # Writes are batched for a specified amount of time, before written to disk and
11
+ # then executed.
12
+ #
13
+ # For a detailed discussion about the problem, see
14
+ # http://www.prevayler.org/wiki.jsp?topic=OvercomingTheWriteBottleneck
15
+ #
16
+ #
17
+ # Usage is identical to normal SnapshotMadeleine, and it can also be used as
18
+ # persister for AutomaticSnapshotMadeleine. (One difference: the log isn't
19
+ # visible on disk until any commands are executed.)
20
+ #
21
+ # You can also use the execute_query method for shared synchronzied queries,
22
+ # for eaay coarse-grained locking of the system.
23
+ #
24
+ # The exclusive lock is only locked during the actual execution of commands and
25
+ # while closing.
26
+ #
27
+ # Keeping both log writes and executes of commands in the originating thread
28
+ # is needed by AutomaticSnapshotPrevayler. Hence the strange SimplisticPipe
29
+ # class.
30
+ #
31
+ # Todo:
32
+ # - It seems like Sync (sync.rb) prefers shared locks. This should probably
33
+ # be changed.
34
+ #
35
+ #
36
+ # Madeleine - Ruby Object Prevalence
37
+ #
38
+ # Copyright(c) Anders Bengtsson 2003
39
+ #
40
+
41
+ require 'madeleine'
42
+ require 'madeleine/clock'
43
+
44
+ include Madeleine::Clock
45
+
46
+ module Madeleine
47
+ module Batch
48
+ class BatchedSnapshotMadeleine < SnapshotMadeleine
49
+
50
+ def initialize(directory_name, marshaller=Marshal, &new_system_block)
51
+ super(directory_name, marshaller, &new_system_block)
52
+ @log_actor = LogActor.launch(self)
53
+ end
54
+
55
+ def execute_command(command)
56
+ verify_command_sane(command)
57
+ queued_command = QueuedCommand.new(command)
58
+ @lock.synchronize(:SH) do
59
+ raise "closed" if @closed
60
+ @logger.store(queued_command)
61
+ end
62
+ queued_command.wait_for
63
+ end
64
+
65
+ def execute_query(query)
66
+ verify_command_sane(query)
67
+ @lock.synchronize(:SH) do
68
+ execute_without_storing(query)
69
+ end
70
+ end
71
+
72
+ def close
73
+ @log_actor.destroy
74
+ @lock.synchronize do
75
+ @logger.close
76
+ @closed = true
77
+ end
78
+ end
79
+
80
+ def flush
81
+ @lock.synchronize do
82
+ @logger.flush
83
+ end
84
+ end
85
+
86
+ def take_snapshot
87
+ @lock.synchronize(:SH) do
88
+ @lock.synchronize do
89
+ @logger.close
90
+ end
91
+ Snapshot.new(@directory_name, system, @marshaller).take
92
+ @logger.reset
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def create_lock
99
+ Sync.new
100
+ end
101
+
102
+ def create_logger(directory_name, log_factory)
103
+ BatchedLogger.new(directory_name, log_factory, self.system)
104
+ end
105
+
106
+ def log_factory
107
+ BatchedLogFactory.new
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ class LogActor
114
+ def self.launch(madeleine, delay=0.01)
115
+ result = new(madeleine, delay)
116
+ result
117
+ end
118
+
119
+ def destroy
120
+ @is_destroyed = true
121
+ if @thread.alive?
122
+ @thread.wakeup
123
+ @thread.join
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def initialize(madeleine, delay)
130
+ @is_destroyed = false
131
+
132
+ madeleine.flush
133
+ @thread = Thread.new {
134
+ until @is_destroyed
135
+ sleep(delay)
136
+ madeleine.flush
137
+ end
138
+ }
139
+ end
140
+ end
141
+
142
+ class BatchedLogFactory
143
+ def create_log(directory_name)
144
+ BatchedLog.new(directory_name)
145
+ end
146
+ end
147
+
148
+ class BatchedLogger < Logger
149
+ def initialize(directory_name, log_factory, system)
150
+ super(directory_name, log_factory)
151
+ @buffer = []
152
+ @system = system
153
+ end
154
+
155
+ def store(queued_command)
156
+ @buffer << queued_command
157
+ end
158
+
159
+ def close
160
+ return if @log.nil?
161
+ flush
162
+ @log.close
163
+ @log = nil
164
+ end
165
+
166
+ def flush
167
+ return if @buffer.empty?
168
+
169
+ open_new_log if @log.nil?
170
+
171
+ if @system.kind_of?(ClockedSystem)
172
+ @buffer.unshift(QueuedTick.new)
173
+ end
174
+
175
+ @buffer.each do |queued_command|
176
+ queued_command.store(@log)
177
+ end
178
+
179
+ @log.flush
180
+
181
+ @buffer.each do |queued_command|
182
+ queued_command.execute(@system)
183
+ end
184
+
185
+ @buffer.clear
186
+ end
187
+ end
188
+
189
+ class BatchedLog < CommandLog
190
+ def store(command)
191
+ Marshal.dump(command, @file)
192
+ end
193
+
194
+ def flush
195
+ @file.flush
196
+ @file.fsync
197
+ end
198
+ end
199
+
200
+ class QueuedCommand
201
+ def initialize(command)
202
+ @command = command
203
+ @pipe = SimplisticPipe.new
204
+ end
205
+
206
+ def store(log)
207
+ @pipe.write(log)
208
+ end
209
+
210
+ def execute(system)
211
+ @pipe.write(system)
212
+ end
213
+
214
+ def wait_for
215
+ @pipe.read do |log|
216
+ log.store(@command)
217
+ end
218
+
219
+ @pipe.read do |system|
220
+ return @command.execute(system)
221
+ end
222
+ end
223
+ end
224
+
225
+ class QueuedTick
226
+ def initialize
227
+ @tick = Tick.new(Time.now)
228
+ end
229
+
230
+ def store(log)
231
+ log.store(@tick)
232
+ end
233
+
234
+ def execute(system)
235
+ @tick.execute(system)
236
+ end
237
+ end
238
+
239
+ class SimplisticPipe
240
+ def initialize
241
+ @receive_lock = Mutex.new.lock
242
+ @consume_lock = Mutex.new.lock
243
+ @message = nil
244
+ end
245
+
246
+ def read
247
+ begin
248
+ wait_for_message_received
249
+
250
+ if block_given?
251
+ yield @message
252
+ else
253
+ return @message
254
+ end
255
+
256
+ ensure
257
+ message_consumed
258
+ end
259
+ end
260
+
261
+ def write(message)
262
+ raise WriteBlockedException unless can_write?
263
+
264
+ @message = message
265
+ message_received
266
+ wait_for_message_consumed
267
+ @message = nil
268
+ end
269
+
270
+ def can_write?
271
+ @message.nil?
272
+ end
273
+
274
+ private
275
+
276
+ def message_received
277
+ @receive_lock.unlock
278
+ end
279
+
280
+ def wait_for_message_received
281
+ @receive_lock.lock
282
+ end
283
+
284
+ def message_consumed
285
+ @consume_lock.unlock
286
+ end
287
+
288
+ def wait_for_message_consumed
289
+ @consume_lock.lock
290
+ end
291
+ end
292
+
293
+ class WriteBlockedException < Exception
294
+ end
295
+ end
296
+ end
297
+
298
+ BatchedSnapshotMadeleine = Madeleine::Batch::BatchedSnapshotMadeleine