pseudo_cleaner 0.0.35 → 0.0.36

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YzliYTc5NmFlMGQyM2E2OTZiNmNkZmI1NWQzOGViMWZmZjRiNWFiZA==
4
+ MjIyM2E5NGJmZGQ4YmRhMzBkOTMzMDEwYmRmY2RjNmZhMmY1YzljNw==
5
5
  data.tar.gz: !binary |-
6
- Nzk4NTNhODc2ZmQ0NGU5YWIyZjVkNmI4YWYyMjBiZmFlYWE3OWQ1ZQ==
6
+ YmM3ZjJmZDFhMmRlMjNmMDU0ZTVjNDM3NzA3YjYxMjRjYTgyMTEwZg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MGZkODUwODZiOGFjMzhjYTY0MjI4M2FmYmFhOWVhNDM5MjM1YjRmODE3N2Y5
10
- N2M1ZjNhNDk3YWZjYzBjYmQ5NTU0MGJhMzQ0ODYyNzI5ZDQ0NmJkZjE2YTc5
11
- NmM0MTU1YzI3YzI2MWU5ZjVhZTljOGQ5NzczZDA0ZGZmNDA3ODU=
9
+ ODAzYzRjZGJjNjIzMTUxOGZlNTAyNjUxYWNmODY1ZGZkNTRhMGUwOWU4Y2Y3
10
+ ZjliZWJlZWQzY2JhMDQ4ODdmYjc1YTUwZGJjYTY5NDAwYjU1YmRmMGE5Njlm
11
+ N2JmNzVhNGJhYzc2N2EzZmY0MjBiNTQyMzY3MTYxYWRjNWZlYWQ=
12
12
  data.tar.gz: !binary |-
13
- NWNmMTQxNzFkZWZhOGI2MGNjZjU4YmFmMmU0NzJhOGI4ZTBiNmFkNDBlMTZk
14
- Y2NjNjU4NTk5NjI2OWIyY2EyMGIxY2UwZGNlOTYwYzhjZjJlZTY1OWRlNGIy
15
- OTNiYTEzMDc4YzUzMzI4YjY1NzQzODgxMTM1MzYyN2U2MTg2MDI=
13
+ MWJkNjdmZjI1MzNlYjQ3MmJjN2Y4MmRiNDYwMGNiZTYxODBiYTM5ZmIxZWFk
14
+ YmY5NTA3OWI5NTFjYWQ0N2ViMDZjNzFjZWE5MzFlNjcxNjBmNjgyMGViMTU1
15
+ ZGYwZjAzZmU5MWE4ZGU3ZGY3OWI5MGQ1NTI1ZmU0MTNhMWJlYTk=
@@ -4,7 +4,7 @@ module PseudoCleaner
4
4
  class MasterCleaner
5
5
  @@suite_cleaner = nil
6
6
  @@cleaner_classes = nil
7
- @@redis_classes = nil
7
+ # @@redis_classes = nil
8
8
  @@cleaner_classes_sorted = false
9
9
  @@report_table = nil
10
10
  @@report_error = false
@@ -72,10 +72,10 @@ module PseudoCleaner
72
72
  end
73
73
  end
74
74
 
75
- def clean_redis(redis)
76
- @@redis_classes ||= []
77
- @@redis_classes << redis
78
- end
75
+ # def clean_redis(redis)
76
+ # @@redis_classes ||= []
77
+ # @@redis_classes << redis
78
+ # end
79
79
 
80
80
  def database_cleaner
81
81
  if Object.const_defined?("ActiveRecord", false) && ActiveRecord.const_defined?("Base", false)
@@ -252,7 +252,7 @@ module PseudoCleaner
252
252
 
253
253
  PseudoCleaner::MasterCleaner.create_table_cleaners
254
254
  PseudoCleaner::MasterCleaner.create_custom_cleaners
255
- PseudoCleaner::MasterCleaner.create_redis_cleaners
255
+ # PseudoCleaner::MasterCleaner.create_redis_cleaners
256
256
  end
257
257
 
258
258
  @@cleaner_classes
@@ -309,13 +309,13 @@ module PseudoCleaner
309
309
  end
310
310
  end
311
311
 
312
- def create_redis_cleaners
313
- if @@redis_classes
314
- @@redis_classes.each do |redis|
315
- PseudoCleaner::MasterCleaner.cleaner_classes << [redis, nil, PseudoCleaner::RedisCleaner]
316
- end
317
- end
318
- end
312
+ # def create_redis_cleaners
313
+ # if @@redis_classes
314
+ # @@redis_classes.each do |redis|
315
+ # PseudoCleaner::MasterCleaner.cleaner_classes << [redis, nil, PseudoCleaner::RedisMonitorCleaner]
316
+ # end
317
+ # end
318
+ # end
319
319
 
320
320
  def find_file_class(seeder_file, seeder_root)
321
321
  check_class = Object
@@ -37,13 +37,20 @@ module PseudoCleaner
37
37
  # check if somebody else needed to do some work. Testing with Rails, I find I have to actually sleep, so I do a
38
38
  # very short time like 0.01.
39
39
  class RedisCleaner
40
- # SUITE_KEY = "PseudoDelete::RedisCleaner:initial_redis_state"
41
-
42
40
  FLUSH_COMMANDS =
43
41
  [
44
42
  "flushall",
45
43
  "flushdb"
46
44
  ]
45
+ SET_COMMANDS =
46
+ [
47
+ "sadd",
48
+ "zadd",
49
+ "srem",
50
+ "zrem",
51
+ "zremrangebyrank",
52
+ "zremrangebyscore"
53
+ ]
47
54
  WRITE_COMMANDS =
48
55
  [
49
56
  "append",
@@ -90,7 +97,6 @@ module PseudoCleaner
90
97
  "rpoplpush",
91
98
  "rpush",
92
99
  "rpushx",
93
- "sadd",
94
100
  "sdiffstore",
95
101
  "set",
96
102
  "setbit",
@@ -101,14 +107,9 @@ module PseudoCleaner
101
107
  "smove",
102
108
  "sort",
103
109
  "spop",
104
- "srem",
105
110
  "sunionstore",
106
- "zadd",
107
111
  "zincrby",
108
112
  "zinterstore",
109
- "zrem",
110
- "zremrangebyrank",
111
- "zremrangebyscore",
112
113
  ]
113
114
  READ_COMMANDS =
114
115
  [
@@ -160,179 +161,168 @@ module PseudoCleaner
160
161
  "zscore",
161
162
  ]
162
163
 
163
- attr_reader :monitor_thread
164
164
  attr_reader :initial_keys
165
165
  attr_accessor :options
166
166
 
167
- class RedisMessage
168
- attr_reader :message
169
- attr_reader :time_stamp
170
- attr_reader :db
171
- attr_reader :host
172
- attr_reader :port
173
- attr_reader :command
174
- attr_reader :cur_pos
175
-
176
- def initialize(message_string)
177
- @message = message_string
167
+ def initialize(start_method, end_method, table, options)
168
+ @initial_keys = SortedSet.new
169
+ @monitor_thread = nil
170
+ @redis_name = nil
171
+ @suite_altered_keys = SortedSet.new
172
+ @updated_keys = SortedSet.new
173
+ @multi_commands = []
174
+ @in_multi = false
175
+ @in_redis_cleanup = false
176
+ @suspend_tracking = false
178
177
 
179
- parse_message
178
+ unless PseudoCleaner::MasterCleaner::VALID_START_METHODS.include?(start_method)
179
+ raise "You must specify a valid start function from: #{PseudoCleaner::MasterCleaner::VALID_START_METHODS}."
180
180
  end
181
+ unless PseudoCleaner::MasterCleaner::VALID_END_METHODS.include?(end_method)
182
+ raise "You must specify a valid end function from: #{PseudoCleaner::MasterCleaner::VALID_END_METHODS}."
183
+ end
184
+
185
+ @options = options
186
+
187
+ @options[:table_start_method] ||= start_method
188
+ @options[:table_end_method] ||= end_method
189
+ @options[:output_diagnostics] ||= PseudoCleaner::Configuration.current_instance.output_diagnostics ||
190
+ PseudoCleaner::Configuration.current_instance.post_transaction_analysis
181
191
 
182
- def parse_message
183
- if @message =~ /[0-9]+\.[0-9]+ \[[0-9]+ [^:]+:[^\]]+\] \"[^\"]+\"/
184
- end_pos = @message.index(" ")
185
- @time_stamp = @message[0..end_pos - 1]
192
+ @redis = table
193
+ end
186
194
 
187
- @cur_pos = end_pos + 2 # " ["
188
- end_pos = @message.index(" ", @cur_pos)
189
- @db = @message[@cur_pos..end_pos - 1].to_i
195
+ # Ruby defines a now deprecated type method so we need to override it here
196
+ # since it will never hit method_missing
197
+ def type(key)
198
+ redis.type(key)
199
+ end
190
200
 
191
- @cur_pos = end_pos + 1
192
- end_pos = @message.index(":", @cur_pos)
193
- @host = @message[@cur_pos..end_pos - 1]
201
+ alias_method :self_respond_to?, :respond_to?
194
202
 
195
- @cur_pos = end_pos + 1
196
- end_pos = @message.index("]", @cur_pos)
197
- @port = @message[@cur_pos..end_pos - 1].to_i
203
+ # emulate Ruby 1.9+ and keep respond_to_missing? logic together.
204
+ def respond_to?(command, include_private=false)
205
+ super or respond_to_missing?(command, include_private)
206
+ end
198
207
 
199
- @cur_pos = end_pos + 2 # "] "
200
- @command = next_value.downcase
201
- else
202
- @command = @message
203
- end
204
- end
208
+ def updated_keys
209
+ @updated_keys ||= SortedSet.new
210
+ end
205
211
 
206
- def next_value
207
- in_quote = (@message[@cur_pos] == '"')
208
- if in_quote
209
- @cur_pos += 1
210
- end_pos = @cur_pos
211
- while (end_pos && end_pos < @message.length)
212
- end_pos = @message.index("\"", end_pos)
212
+ def method_missing(command, *args, &block)
213
+ normalized_command = command.to_s.downcase
213
214
 
214
- num_backslashes = 0
215
- back_pos = end_pos
215
+ if redis.respond_to?(normalized_command)
216
+ if normalized_command == "pipelined" ||
217
+ (normalized_command == "multi" && block)
218
+ @in_multi = true
219
+ normalized_command = "exec"
220
+ end
216
221
 
217
- while @message[back_pos - 1] == "\\"
218
- num_backslashes += 1
219
- back_pos -= 1
220
- end
222
+ response = redis.send(command, *args, &block)
221
223
 
222
- break if (num_backslashes % 2) == 0
223
- end_pos += 1
224
- end
224
+ if @in_multi && !(["exec", "discard"].include?(normalized_command))
225
+ @multi_commands << [normalized_command, *args]
225
226
  else
226
- end_pos = @message.index(" ", @cur_pos)
227
+ process_command(response, normalized_command, *args)
227
228
  end
228
- the_value = @message[@cur_pos..end_pos - 1]
229
- end_pos += 1 if in_quote
230
229
 
231
- @cur_pos = end_pos + 1
232
-
233
- the_value.gsub("\\\\", "\\").gsub("\\\"", "\"")
230
+ response
231
+ else
232
+ super
234
233
  end
234
+ end
235
235
 
236
- def keys
237
- unless defined?(@message_keys)
238
- @message_keys = []
239
-
240
- if Redis::Namespace::COMMANDS.include? command
241
- handling = Redis::Namespace::COMMANDS[command.to_s.downcase]
236
+ def process_command(response, *args)
237
+ unless @in_redis_cleanup || @suspend_tracking
238
+ if "multi" == args[0]
239
+ @in_multi = true
240
+ @multi_commands = []
241
+ elsif ["exec", "pipelined"].include?(args[0])
242
+ begin
243
+ raise "exec response does not match sent commands" unless response.length == @multi_commands.length
244
+
245
+ response.each_with_index do |command_response, index|
246
+ process_command(command_response, *(@multi_commands[index]))
247
+ end
248
+ ensure
249
+ @in_multi = false
250
+ @multi_commands = []
251
+ end
252
+ elsif "discard" == args[0]
253
+ @in_multi = false
254
+ @multi_commands = []
255
+ elsif WRITE_COMMANDS.include?(args[0])
256
+ updated_keys.merge(extract_keys(*args))
257
+ elsif SET_COMMANDS.include?(args[0])
258
+ if [true, false].include?(response) ? response : (response > 0)
259
+ updated_keys.merge(extract_keys(*args))
260
+ end
261
+ end
262
+ end
263
+ end
242
264
 
243
- (before, after) = handling
265
+ def respond_to_missing?(command, include_all=false)
266
+ return true if WRITE_COMMANDS.include?(command.to_s.downcase)
244
267
 
245
- case before
246
- when :first
247
- @message_keys << next_value
268
+ # blind passthrough is deprecated and will be removed in 2.0
269
+ if redis.respond_to?(command, include_all)
270
+ return true
271
+ end
248
272
 
249
- when :all
250
- while @cur_pos < @message.length
251
- @message_keys << next_value
252
- end
273
+ defined?(super) && super
274
+ end
253
275
 
254
- when :exclude_first
255
- next_value
256
- while @cur_pos < @message.length
257
- @message_keys << next_value
258
- end
276
+ def extract_keys(command, *args)
277
+ handling = Redis::Namespace::COMMANDS[command.to_s.downcase]
278
+ message_keys = []
259
279
 
260
- when :exclude_last
261
- while @cur_pos < @message.length
262
- @message_keys << next_value
263
- end
264
- @message_keys.delete_at(@message_keys.length - 1)
265
-
266
- when :exclude_options
267
- options = ["weights", "aggregate", "sum", "min", "max"]
268
- while @cur_pos < @message.length
269
- @message_keys << next_value
270
- if options.include?(@message_keys[-1].downcase)
271
- @message_keys.delete_at(@message_keys.length - 1)
272
- break
273
- end
274
- end
280
+ (before, after) = handling
275
281
 
276
- when :alternate
277
- while @cur_pos < @message.length
278
- @message_keys << next_value
279
- next_value
280
- end
282
+ case before
283
+ when :first
284
+ message_keys << args[0] if args[0]
281
285
 
282
- when :sort
283
- next_value
286
+ when :all
287
+ args.each do |arg|
288
+ message_keys << arg
289
+ end
284
290
 
285
- while @cur_pos < @message.length
286
- a_value = next_value
287
- if a_value.downcase == "store"
288
- @message_keys[0] = next_value
289
- end
290
- end
291
+ when :exclude_first
292
+ args.each do |arg|
293
+ message_keys << arg
294
+ end
295
+ message_keys.shift
291
296
 
292
- # when :eval_style
293
- #
294
- # when :scan_style
295
- end
297
+ when :exclude_last
298
+ args.each do |arg|
299
+ message_keys << arg
296
300
  end
297
- end
301
+ message_keys.pop unless message_keys.length == 1
298
302
 
299
- @message_keys
300
- end
303
+ when :exclude_options
304
+ args.each do |arg|
305
+ message_keys << arg unless arg.is_a?(Hash)
306
+ end
301
307
 
302
- def to_s
303
- {
304
- time_stamp: time_stamp,
305
- db: db,
306
- host: host,
307
- port: port,
308
- command: command,
309
- message: message,
310
- cur_pos: cur_pos
311
- }.to_s
312
- end
313
- end
308
+ when :alternate
309
+ args.each_with_index do |arg, i|
310
+ message_keys << arg if i.even?
311
+ end
314
312
 
315
- def initialize(start_method, end_method, table, options)
316
- @initial_keys = SortedSet.new
317
- @monitor_thread = nil
318
- @redis_name = nil
319
- @suite_altered_keys = SortedSet.new
313
+ when :sort
314
+ if args[-1].is_a?(Hash)
315
+ if args[1][:store]
316
+ message_keys << args[1][:store]
317
+ end
318
+ end
320
319
 
321
- unless PseudoCleaner::MasterCleaner::VALID_START_METHODS.include?(start_method)
322
- raise "You must specify a valid start function from: #{PseudoCleaner::MasterCleaner::VALID_START_METHODS}."
323
- end
324
- unless PseudoCleaner::MasterCleaner::VALID_END_METHODS.include?(end_method)
325
- raise "You must specify a valid end function from: #{PseudoCleaner::MasterCleaner::VALID_END_METHODS}."
320
+ # when :eval_style
321
+ #
322
+ # when :scan_style
326
323
  end
327
324
 
328
- @options = options
329
-
330
- @options[:table_start_method] ||= start_method
331
- @options[:table_end_method] ||= end_method
332
- @options[:output_diagnostics] ||= PseudoCleaner::Configuration.current_instance.output_diagnostics ||
333
- PseudoCleaner::Configuration.current_instance.post_transaction_analysis
334
-
335
- @redis = table
325
+ message_keys
336
326
  end
337
327
 
338
328
  def <=>(right_object)
@@ -359,15 +349,19 @@ module PseudoCleaner
359
349
  def suite_start test_strategy
360
350
  @test_strategy ||= test_strategy
361
351
 
362
- # if redis.type(PseudoCleaner::RedisCleaner::SUITE_KEY) == "set"
363
- # @initial_keys = SortedSet.new(redis.smembers(PseudoCleaner::RedisCleaner::SUITE_KEY))
364
- # report_end_of_suite_state "before suite start"
365
- # end
366
- # redis.del PseudoCleaner::RedisCleaner::SUITE_KEY
367
-
368
352
  start_monitor
369
353
  end
370
354
 
355
+ def suspend_tracking(&block)
356
+ begin
357
+ @suspend_tracking = true
358
+
359
+ block.yield
360
+ ensure
361
+ @suspend_tracking = false
362
+ end
363
+ end
364
+
371
365
  def test_start test_strategy
372
366
  @test_strategy ||= test_strategy
373
367
 
@@ -407,21 +401,12 @@ module PseudoCleaner
407
401
 
408
402
  def suite_end test_strategy
409
403
  report_end_of_suite_state "suite end"
410
-
411
- if monitor_thread
412
- monitor_thread.kill
413
- @monitor_thread = nil
414
- end
415
404
  end
416
405
 
417
406
  def reset_suite
418
407
  report_end_of_suite_state "reset suite"
419
408
 
420
- if monitor_thread
421
- monitor_thread.kill
422
- @monitor_thread = nil
423
- start_monitor
424
- end
409
+ start_monitor
425
410
  end
426
411
 
427
412
  def ignore_regexes
@@ -485,14 +470,6 @@ module PseudoCleaner
485
470
  end
486
471
  end
487
472
 
488
- def synchronize_key
489
- @synchronize_key ||= "redis_cleaner::synchronization_key_#{rand(1..1_000_000_000_000_000_000)}_#{rand(1..1_000_000_000_000_000_000)}"
490
- end
491
-
492
- def synchronize_end_key
493
- @synchronize_end_key ||= "redis_cleaner::synchronization_end_key_#{rand(1..1_000_000_000_000_000_000)}_#{rand(1..1_000_000_000_000_000_000)}"
494
- end
495
-
496
473
  def report_end_of_suite_state report_reason
497
474
  current_keys = SortedSet.new(redis.keys)
498
475
 
@@ -514,30 +491,24 @@ module PseudoCleaner
514
491
  end
515
492
 
516
493
  def synchronize_test_values(&block)
517
- updated_values = nil
494
+ updated_values = updated_keys.dup
518
495
 
519
- if monitor_thread
520
- redis.setex(synchronize_key, 1, true)
521
- updated_values = queue.pop
522
- end
523
-
524
- block.yield updated_values
496
+ @in_redis_cleanup = true
525
497
 
526
- redis.setex(synchronize_end_key, 1, true)
527
- end
498
+ begin
499
+ block.yield updated_values
500
+ ensure
501
+ @in_redis_cleanup = false
502
+ end
528
503
 
529
- def queue
530
- @queue ||= Queue.new
504
+ @updated_keys = SortedSet.new
531
505
  end
532
506
 
533
507
  def start_monitor
534
508
  cleaner_class = self
535
509
 
536
510
  @initial_keys = SortedSet.new(redis.keys)
537
- # @initial_keys.add(PseudoCleaner::RedisCleaner::SUITE_KEY)
538
- # @initial_keys.each do |key_value|
539
- # redis.sadd(PseudoCleaner::RedisCleaner::SUITE_KEY, key_value)
540
- # end
511
+
541
512
  if @options[:output_diagnostics]
542
513
  if PseudoCleaner::MasterCleaner.report_table
543
514
  Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table,
@@ -550,60 +521,6 @@ module PseudoCleaner
550
521
  PseudoCleaner::Logger.write(" Initial keys count - #{@initial_keys.count}")
551
522
  end
552
523
  end
553
-
554
- unless @monitor_thread
555
- @monitor_thread = Thread.new do
556
- in_redis_cleanup = false
557
- updated_keys = SortedSet.new
558
-
559
- monitor_redis = Redis.new(cleaner_class.redis.client.options)
560
- redis_options = monitor_redis.client.options.with_indifferent_access
561
- cleaner_class_db = redis_options[:db]
562
-
563
- monitor_redis.monitor do |message|
564
- redis_message = RedisMessage.new message
565
-
566
- if redis_message.db == cleaner_class_db
567
- process_command = true
568
-
569
- if redis_message.command == "setex"
570
- if redis_message.keys[0] == cleaner_class.synchronize_key
571
- process_command = false
572
-
573
- in_redis_cleanup = true
574
- return_values = updated_keys
575
- updated_keys = SortedSet.new
576
- cleaner_class.queue << return_values
577
- elsif redis_message.keys[0] == cleaner_class.synchronize_end_key
578
- in_redis_cleanup = false
579
- cleaner_class.monitor_thread[:updated] = nil
580
- process_command = false
581
- end
582
- elsif redis_message.command == "del"
583
- if in_redis_cleanup
584
- process_command = false
585
- end
586
- end
587
-
588
- if process_command
589
- # flush...
590
- if PseudoCleaner::RedisCleaner::WRITE_COMMANDS.include? redis_message.command
591
- updated_keys.merge(redis_message.keys)
592
- elsif PseudoCleaner::RedisCleaner::FLUSH_COMMANDS.include? redis_message.command
593
- # Not sure I can get the keys at this point...
594
- # updated_keys.merge(cleaner_class.redis.keys)
595
- end
596
- end
597
- elsif "flushall" == redis_message.command
598
- # Not sure I can get the keys at this point...
599
- # updated_keys.merge(cleaner_class.redis.keys)
600
- end
601
- end
602
- end
603
-
604
- sleep(0.01)
605
- redis.get(synchronize_key)
606
- end
607
524
  end
608
525
 
609
526
  def report_record(key_name)