hprevalence 0.1.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/README ADDED
@@ -0,0 +1,18 @@
1
+
2
+ Thanks for using HPrevalence!
3
+
4
+ Don't forget:
5
+
6
+ - You'll need RubyGems to install it:
7
+ Grab it http://rubygems.rubyforge.org
8
+
9
+ And HPrevalence depends on
10
+
11
+ - Guid
12
+ http://guid.rubyforge.org
13
+
14
+ - Rake (if you want to execute the test cases)
15
+ http://rake.rubyforge.org
16
+
17
+
18
+ Enjoy!
@@ -0,0 +1,243 @@
1
+
2
+ require 'thread'
3
+ require 'guid'
4
+ require 'transparent'
5
+ require 'internal/command_logger'
6
+ require 'internal/iomanager'
7
+ require 'internal/serializer'
8
+ require 'internal/store_manager'
9
+
10
+ module HPrevalence
11
+
12
+ #
13
+ class EngineBuilder
14
+
15
+ def self.build( target_dir, &system_init )
16
+ system = system_init.call
17
+ SimpleEngine.new( target_dir, system )
18
+ end
19
+
20
+ def self.build_transparent( target_dir, &system_init )
21
+ system = system_init.call
22
+ TransparentEngine.new( target_dir, system )
23
+ end
24
+
25
+ end
26
+
27
+ #
28
+ class SimpleEngine
29
+
30
+ attr_reader :system, :target_dir
31
+
32
+ def initialize( target_dir, system, store_manager = StorageManager.new(target_dir) )
33
+ @lock = Mutex.new
34
+ @target_dir, @system, @store_manager = target_dir, system, store_manager
35
+ @system = @store_manager.restore( system() )
36
+ end
37
+
38
+ def execute_command( command )
39
+ raise 'Invalid command' unless command.respond_to?( :execute )
40
+ @lock.synchronize {
41
+ @store_manager.store( command )
42
+ return command.execute( @system )
43
+ }
44
+ end
45
+
46
+ def close
47
+ @store_manager.close
48
+ end
49
+
50
+ def take_snapshot
51
+ @lock.synchronize {
52
+ @store_manager.take_snapshot @system
53
+ }
54
+ end
55
+
56
+ end
57
+
58
+ #
59
+ # Holds only one system
60
+ #
61
+ class TransparentEngine < SimpleEngine
62
+ @@engines = Hash.new()
63
+ @@class_lock = Mutex.new
64
+
65
+ attr_reader :engine_id
66
+
67
+ def initialize( target_dir, system )
68
+ @proxies_lock = Mutex.new
69
+ @engine_id = Guid.new()
70
+
71
+ TransparentEngine.register_engine( self )
72
+
73
+ @proxies = []
74
+ add_engine_id_method( system )
75
+ store_manager = TransparentStorageManager.new(self, target_dir)
76
+ within_engine {
77
+ @proxied_system = HPrevalence::Transparent::TransparentProxy.new( system )
78
+ }
79
+
80
+ super( target_dir, system, store_manager )
81
+ end
82
+
83
+ def system
84
+ @proxied_system
85
+ end
86
+
87
+ def close
88
+ super
89
+ TransparentEngine.unregister_engine( self )
90
+ end
91
+
92
+ def take_snapshot
93
+ @lock.synchronize {
94
+ begin
95
+ Thread.current[:references] = Hash.new
96
+ within_engine {
97
+ @store_manager.take_snapshot @proxied_system
98
+ }
99
+ ensure
100
+ Thread.current[:references] = nil
101
+ end
102
+ }
103
+ end
104
+
105
+ def execute_command(command)
106
+ super(command)
107
+ end
108
+
109
+ def register_proxy(proxy)
110
+ # TODO: Synchronization here?
111
+ @proxies << proxy
112
+ @proxies.length - 1
113
+ end
114
+
115
+ def proxy_by_id( id, related_symbol = nil, parent_proxy_id = nil )
116
+ p = @proxies[ id ]
117
+ return p unless p.nil?
118
+ if !related_symbol.nil? && !parent_proxy_id.nil?
119
+ return create_nested_proxy( parent_proxy_id, related_symbol )
120
+ end
121
+ nil
122
+ end
123
+
124
+ def create_nested_proxy( parent_proxy_id, related_symbol )
125
+ @proxies_lock.synchronize {
126
+ parent_proxy = proxy_by_id( parent_proxy_id )
127
+ target = parent_proxy.target
128
+ nested_target = target.send( related_symbol )
129
+ new_proxy = HPrevalence::Transparent::NestedTransparentProxy.new( nested_target, related_symbol, parent_proxy_id )
130
+ target._set_proxy( related_symbol, new_proxy )
131
+ return new_proxy
132
+ }
133
+ end
134
+
135
+ def within_engine()
136
+ begin
137
+ Thread.current[:engine] = self
138
+ yield
139
+ ensure
140
+ Thread.current[:engine] = nil
141
+ end
142
+ end
143
+
144
+ def restore(proxy)
145
+ proxy.engine_id = @engine_id
146
+ p = proxy_by_id(proxy.proxy_id)
147
+ if (p.nil?)
148
+ proxy.proxy_id = register_proxy(proxy)
149
+ p = proxy
150
+ end
151
+ p
152
+ end
153
+
154
+ def self.create_nested_proxy( parent_proxy_id, symbol, engine_id )
155
+ within_engine( engine_id ) { |engine|
156
+ engine.create_nested_proxy( parent_proxy_id, symbol )
157
+ }
158
+ end
159
+
160
+ def self.within_engine( engine_id )
161
+ engine = @@engines[ engine_id ]
162
+ raise 'Engine not found' if engine.nil?
163
+ begin
164
+ # puts 'engine>'
165
+ Thread.current[:engine] = engine
166
+ yield(engine)
167
+ ensure
168
+ Thread.current[:engine] = nil
169
+ # puts 'engine<'
170
+ end
171
+ end
172
+
173
+ def self.current()
174
+ engine = Thread.current[:engine]
175
+
176
+ if block_given?
177
+ # puts 'engine.current'
178
+ raise 'Engine not found' if engine.nil?
179
+ yield( engine )
180
+ # puts 'end.current'
181
+ else
182
+ return engine
183
+ end
184
+ end
185
+
186
+ protected
187
+
188
+ def self.register_engine( engine )
189
+ @@class_lock.synchronize {
190
+ @@engines[engine.engine_id] = engine
191
+ }
192
+ end
193
+
194
+ def self.unregister_engine( engine )
195
+ @@class_lock.synchronize {
196
+ @@engines.delete(engine.engine_id)
197
+ }
198
+ end
199
+
200
+ def add_engine_id_method( system )
201
+ temp_id = @engine_id
202
+ system.instance_eval {
203
+ @engine_id = temp_id
204
+ }
205
+ # (class << system; self;end).class_eval do
206
+ # define_method(:_engine_id) { @engine_id }
207
+ # end
208
+ end
209
+
210
+ end
211
+
212
+ class PrevalenceService
213
+ attr_reader :engine
214
+
215
+ def self.create_service( snapshot_interval = 60 * 60 * 1000, &engine_init )
216
+ engine = engine_init.call
217
+ raise 'Engine not created' if engine.nil?
218
+ PrevalenceService.new( engine, snapshot_interval )
219
+ end
220
+
221
+ def system
222
+ @engine.system
223
+ end
224
+
225
+ protected
226
+
227
+ def initialize( engine, snapshot_interval )
228
+ @engine = engine
229
+ @snapshot_interval = snapshot_interval
230
+ end
231
+
232
+ def start_snapshot_thread
233
+ Thread.new(self) {
234
+ while true
235
+ sleep(@snapshot_interval)
236
+ engine.take_snapshot
237
+ end
238
+ }
239
+ end
240
+
241
+ end
242
+
243
+ end
@@ -0,0 +1,98 @@
1
+
2
+ class BinaryCommandSerializer
3
+
4
+ def initialize( io )
5
+ raise 'Invalid stream' if io.nil?
6
+ @io = io
7
+ @io.binmode
8
+ # register_finalize()
9
+ end
10
+
11
+ def <<(command)
12
+ raise 'Invalid command' if command.nil?
13
+ Marshal.dump( command, @io )
14
+ @io.flush
15
+ @io.fsync
16
+ end
17
+
18
+ def self.restore_from_io( io, system )
19
+ # Theorically this should work and its more elegant than the current version
20
+ # Marshal.load( io ) { |command|
21
+ # command.execute( system )
22
+ # }
23
+ while( !io.eof? )
24
+ command = Marshal.load( io )
25
+ command.execute( system )
26
+ end
27
+ end
28
+
29
+ def close
30
+ @io.close
31
+ # unregister_finalize
32
+ end
33
+ #
34
+ # protected
35
+ #
36
+ # def register_finalize
37
+ # ObjectSpace.define_finalizer( self ) {
38
+ # |logger|
39
+ # logger.close
40
+ # }
41
+ # end
42
+ #
43
+ # def unregister_finalize
44
+ # ObjectSpace.undefine_finalizer( self )
45
+ # end
46
+
47
+ end
48
+
49
+ class CommandLogger
50
+
51
+ Logger_Extension = 'commandlog'
52
+
53
+ attr_reader :current_file_name
54
+
55
+ def initialize( iomanager )
56
+ @iomanager = iomanager
57
+ end
58
+
59
+ def create_command_store()
60
+ stream = @iomanager.create_new_stream( Logger_Extension )
61
+ @current_file_name = stream.full_name
62
+ BinaryCommandSerializer.new( stream )
63
+ end
64
+
65
+ def log_files
66
+ @iomanager.list( Logger_Extension )
67
+ end
68
+
69
+ # removes all command_logs as a snapshot has been taken
70
+ def reset_commands()
71
+ streams = @iomanager.obtain_ordered_read_streams( Logger_Extension )
72
+
73
+ streams.each do |stream|
74
+ stream.close
75
+ File.delete stream.full_name
76
+ end
77
+
78
+ end
79
+
80
+ def restore( system )
81
+ streams = @iomanager.obtain_ordered_read_streams( Logger_Extension )
82
+
83
+ begin
84
+
85
+ streams.each do |stream|
86
+ BinaryCommandSerializer.restore_from_io( stream, system )
87
+ end
88
+
89
+ ensure
90
+ streams.each do |stream|
91
+ stream.close
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
@@ -0,0 +1,83 @@
1
+
2
+
3
+ class FileIOManager
4
+
5
+ attr_reader :positions, :target_dir
6
+
7
+ def initialize( target_dir, positions = 12 )
8
+ @target_dir, @positions = target_dir, positions
9
+ end
10
+
11
+ def create_new_stream( extension )
12
+ file_name = obtain_next_available( extension )
13
+ create_write_stream( file_name )
14
+ end
15
+
16
+ def list( extension )
17
+ unless File.exist?( @target_dir )
18
+ return []
19
+ end
20
+ dir = File.join( @target_dir, "*.#{extension}" )
21
+ valid_entries = Dir.entries( @target_dir ).select do |entry|
22
+ entry =~ /^\d{#{positions}}\.#{extension}$/
23
+ end
24
+ valid_entries.sort
25
+ end
26
+
27
+ def obtain_ordered_read_streams( extension )
28
+ streams = []
29
+
30
+ list( extension ).each do |file|
31
+ file_name = File.join( @target_dir, file )
32
+ streams << create_read_stream( file_name )
33
+ end
34
+
35
+ streams
36
+ end
37
+
38
+ def obtain_latest_read_stream( extension )
39
+ files = list( extension )
40
+ return nil if files.length == 0
41
+
42
+ file_name = File.join( @target_dir, files.max )
43
+ create_read_stream( file_name )
44
+ end
45
+
46
+ protected
47
+
48
+ def create_read_stream( file_name )
49
+ file = File.new( file_name, "rb" )
50
+ add_helper_methods( file, file_name )
51
+ end
52
+
53
+ def create_write_stream( file_name )
54
+ file = File.new( file_name, "wb" )
55
+ add_helper_methods( file, file_name )
56
+ end
57
+
58
+ def add_helper_methods( stream, file_name )
59
+ (class << stream; self; end).class_eval do
60
+ define_method(:full_name) { file_name }
61
+ define_method(:basename) { File.basename(file_name) }
62
+ end
63
+ stream
64
+ end
65
+
66
+ def obtain_hightest_num( extension )
67
+ val = 0
68
+ list( extension ).each do |file|
69
+ new_val = file.slice( 0, positions ).to_i
70
+ if new_val > val
71
+ val = new_val
72
+ end
73
+ end
74
+ val
75
+ end
76
+
77
+ def obtain_next_available( extension )
78
+ val = obtain_hightest_num( extension )
79
+ sprintf("%s/%0#{positions}d.%s", @target_dir, val + 1, extension)
80
+ end
81
+
82
+ end
83
+
@@ -0,0 +1,68 @@
1
+
2
+ class ObjectGraph
3
+
4
+ def initialize()
5
+ @root = nil
6
+ @registry = Hash.new()
7
+ end
8
+
9
+ def self.build( node )
10
+ graph = ObjectGraph.new()
11
+ graph.root = node
12
+ graph
13
+ end
14
+
15
+ def root=( node )
16
+ @root = GraphItem.new( self, node )
17
+ end
18
+
19
+ def root
20
+ @root.object
21
+ end
22
+
23
+ def object_reference( var )
24
+ graph_item = @registry[ var ]
25
+ if (graph_item.nil?)
26
+ graph_item = GraphItem.new( self, var )
27
+ @registry[ var ] = graph_item
28
+ end
29
+ graph_item
30
+ end
31
+
32
+ end
33
+
34
+ class GraphItem
35
+
36
+ def initialize( graph, node )
37
+ @instance_vars = Hash.new
38
+ @node_type = node.class
39
+
40
+ node.instance_variables.each do |var|
41
+ name = var.slice(1, var.length - 1)
42
+ @instance_vars[ name ] = graph.object_reference( node.instance_eval(var) )
43
+ end
44
+ end
45
+
46
+ def object
47
+ obj = @node_type.allocate
48
+ restore_instance_vars obj
49
+ obj
50
+ end
51
+
52
+ protected
53
+
54
+ def restore_instance_vars(obj)
55
+ @instance_vars.each do |name, value|
56
+
57
+ var = value.object
58
+
59
+ setter = name + "="
60
+ if obj.respond_to?(setter)
61
+ obj.__send__(setter, var)
62
+ else
63
+ obj.instance_eval("@#{ name } = var")
64
+ end
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,83 @@
1
+
2
+ class DefaultModelSerializer
3
+
4
+ Snapshot_Extension = 'snapshot'
5
+
6
+ def initialize( iomanager )
7
+ @iomanager = iomanager
8
+ end
9
+
10
+ def dump( object_model )
11
+ begin
12
+ stream = @iomanager.create_new_stream( Snapshot_Extension )
13
+ Marshal.dump( object_model, stream )
14
+ ensure
15
+ stream.close
16
+ end
17
+ end
18
+
19
+ def load()
20
+ object_model = nil
21
+ stream = @iomanager.obtain_latest_read_stream( Snapshot_Extension )
22
+ return nil if stream.nil?
23
+
24
+ begin
25
+ object_model = Marshal.load( stream )
26
+ ensure
27
+ stream.close
28
+ end
29
+
30
+ object_model
31
+ end
32
+
33
+ end
34
+
35
+ #class SmartModelSerializer
36
+ #
37
+ # Snapshot_Extension = 'ssnapshot'
38
+ #
39
+ # def initialize( iomanager )
40
+ # @iomanager = iomanager
41
+ # end
42
+ #
43
+ # def dump( object_model )
44
+ #
45
+ # begin
46
+ # stream = @iomanager.create_new_stream( Snapshot_Extension )
47
+ # graph = ObjectGraph.build( object_model )
48
+ # write_object_tree( stream, graph )
49
+ # ensure
50
+ # stream.close
51
+ # end
52
+ #
53
+ # end
54
+ #
55
+ # def load()
56
+ #
57
+ # object_model = nil
58
+ #
59
+ # begin
60
+ # stream = @iomanager.obtain_latest_read_stream( Snapshot_Extension )
61
+ # graph = read_object_tree( stream )
62
+ # object_model = graph.root
63
+ # ensure
64
+ # stream.close
65
+ # end
66
+ #
67
+ # object_model
68
+ #
69
+ # end
70
+ #
71
+ # protected
72
+ #
73
+ # def write_object_tree( stream, object )
74
+ # Marshal.dump( object, stream )
75
+ # end
76
+ #
77
+ # def read_object_tree( stream )
78
+ # Marshal.load( stream )
79
+ # end
80
+ #
81
+ #end
82
+
83
+
@@ -0,0 +1,74 @@
1
+
2
+ module HPrevalence
3
+
4
+ class StorageManager
5
+
6
+ def initialize( target_dir, iomanager = FileIOManager.new(target_dir), logger = CommandLogger.new(iomanager), model_serializer = DefaultModelSerializer.new(iomanager) )
7
+ @target_dir, @model_serializer = target_dir, model_serializer
8
+ @logger = logger
9
+ @comand_store = nil
10
+ end
11
+
12
+ def restore( system )
13
+ system = restore_snapshot( system )
14
+ restore_commands( system )
15
+ system
16
+ end
17
+
18
+ def store( command )
19
+ ensure_command_store
20
+ @comand_store << command
21
+ end
22
+
23
+ def take_snapshot( system )
24
+ @model_serializer.dump( system )
25
+ close()
26
+ @logger.reset_commands
27
+ end
28
+
29
+ def close
30
+ @comand_store.close unless @comand_store.nil?
31
+ @comand_store = nil
32
+ end
33
+
34
+ protected
35
+
36
+ def ensure_command_store
37
+ @comand_store ||= @logger.create_command_store()
38
+ end
39
+
40
+ def restore_commands( system )
41
+ @logger.restore( system )
42
+ end
43
+
44
+ def restore_snapshot( system )
45
+ system_new = @model_serializer.load
46
+ if !system_new.nil?
47
+ system = system_new
48
+ end
49
+ system
50
+ end
51
+
52
+ end
53
+
54
+ #
55
+ #
56
+ #
57
+ class TransparentStorageManager < StorageManager
58
+
59
+ def initialize( engine, target_dir, iomanager = FileIOManager.new(target_dir), logger = CommandLogger.new(iomanager), model_serializer = DefaultModelSerializer.new(iomanager) )
60
+ super( target_dir, iomanager, logger, model_serializer )
61
+ @engine = engine
62
+ end
63
+
64
+ def restore( system )
65
+ @engine.within_engine {
66
+ system = restore_snapshot( system )
67
+ restore_commands( system )
68
+ }
69
+ system
70
+ end
71
+
72
+ end
73
+
74
+ end