madeleine 0.7.1

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/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