pseudo_cleaner 0.0.35 → 0.0.36
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.
- checksums.yaml +8 -8
- data/lib/pseudo_cleaner/master_cleaner.rb +13 -13
- data/lib/pseudo_cleaner/redis_cleaner.rb +156 -239
- data/lib/pseudo_cleaner/redis_monitor_cleaner.rb +665 -0
- data/lib/pseudo_cleaner/version.rb +1 -1
- data/spec/redis_monitor_cleaner_spec.rb +162 -0
- metadata +5 -4
- data/spec/redis_cleaner_spec.rb +0 -162
@@ -0,0 +1,665 @@
|
|
1
|
+
require "redis"
|
2
|
+
require "redis-namespace"
|
3
|
+
require "pseudo_cleaner/master_cleaner"
|
4
|
+
require "pseudo_cleaner/configuration"
|
5
|
+
require "pseudo_cleaner/logger"
|
6
|
+
require "colorize"
|
7
|
+
|
8
|
+
module PseudoCleaner
|
9
|
+
##
|
10
|
+
# This class "cleans" a single redis connection.
|
11
|
+
#
|
12
|
+
# The cleaning is done by opening a monitor connection on redis and monitoring it for any actions that change values
|
13
|
+
# in redis.
|
14
|
+
#
|
15
|
+
# Long term, I was thinking about keeping some stats on the redis calls, but for now, I'm not.
|
16
|
+
#
|
17
|
+
# The cleaner roughly works as follows:
|
18
|
+
# * Get a list of all existing keys in the database as a level starting point.
|
19
|
+
# * Start a monitor
|
20
|
+
# * The monitor records any key that is updated or changed
|
21
|
+
# * When a test ends
|
22
|
+
# * Ask the monitor for a list of all changed keys.
|
23
|
+
# * The monitor resets the list of all changed keys.
|
24
|
+
# * When the suite ends
|
25
|
+
# * Get a list of all of the keys
|
26
|
+
# * Compare that list to the starting level
|
27
|
+
#
|
28
|
+
# Again, like the TableCleaner, if items are updated, this won't be able to "fix" that, but it will report on it.
|
29
|
+
#
|
30
|
+
# At this time, my code only pays attention to one database. It ignores other databases. We could extend things
|
31
|
+
# and have the monitor watch multiple databases. I'll have to do that later if I find a need.
|
32
|
+
|
33
|
+
# NOTE: Like the database cleaner, if the test is interrupted and test_end isn't called, the redis database may be
|
34
|
+
# left in an uncertain state.
|
35
|
+
|
36
|
+
# I'm not a huge fan of sleeps. In the non-rails world, I used to be able to do a sleep(0) to signal the system to
|
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
|
+
# very short time like 0.01.
|
39
|
+
class RedisMonitorCleaner
|
40
|
+
# SUITE_KEY = "PseudoDelete::RedisMonitorCleaner:initial_redis_state"
|
41
|
+
|
42
|
+
FLUSH_COMMANDS =
|
43
|
+
[
|
44
|
+
"flushall",
|
45
|
+
"flushdb"
|
46
|
+
]
|
47
|
+
WRITE_COMMANDS =
|
48
|
+
[
|
49
|
+
"append",
|
50
|
+
"bitop",
|
51
|
+
"blpop",
|
52
|
+
"brpop",
|
53
|
+
"brpoplpush",
|
54
|
+
"decr",
|
55
|
+
"decrby",
|
56
|
+
"del",
|
57
|
+
"expire",
|
58
|
+
"expireat",
|
59
|
+
"getset",
|
60
|
+
"hset",
|
61
|
+
"hsetnx",
|
62
|
+
"hincrby",
|
63
|
+
"hincrbyfloat",
|
64
|
+
"hmset",
|
65
|
+
"hdel",
|
66
|
+
"incr",
|
67
|
+
"incrby",
|
68
|
+
"incrbyfloat",
|
69
|
+
"linsert",
|
70
|
+
"lpop",
|
71
|
+
"lpush",
|
72
|
+
"lpushx",
|
73
|
+
"lrem",
|
74
|
+
"lset",
|
75
|
+
"ltrim",
|
76
|
+
"mapped_hmset",
|
77
|
+
"mapped_mset",
|
78
|
+
"mapped_msetnx",
|
79
|
+
"move",
|
80
|
+
"mset",
|
81
|
+
"msetnx",
|
82
|
+
"persist",
|
83
|
+
"pexpire",
|
84
|
+
"pexpireat",
|
85
|
+
"psetex",
|
86
|
+
"rename",
|
87
|
+
"renamenx",
|
88
|
+
"restore",
|
89
|
+
"rpop",
|
90
|
+
"rpoplpush",
|
91
|
+
"rpush",
|
92
|
+
"rpushx",
|
93
|
+
"sadd",
|
94
|
+
"sdiffstore",
|
95
|
+
"set",
|
96
|
+
"setbit",
|
97
|
+
"setex",
|
98
|
+
"setnx",
|
99
|
+
"setrange",
|
100
|
+
"sinterstore",
|
101
|
+
"smove",
|
102
|
+
"sort",
|
103
|
+
"spop",
|
104
|
+
"srem",
|
105
|
+
"sunionstore",
|
106
|
+
"zadd",
|
107
|
+
"zincrby",
|
108
|
+
"zinterstore",
|
109
|
+
"zrem",
|
110
|
+
"zremrangebyrank",
|
111
|
+
"zremrangebyscore",
|
112
|
+
]
|
113
|
+
READ_COMMANDS =
|
114
|
+
[
|
115
|
+
"bitcount",
|
116
|
+
"bitop",
|
117
|
+
"dump",
|
118
|
+
"exists",
|
119
|
+
"get",
|
120
|
+
"getbit",
|
121
|
+
"getrange",
|
122
|
+
"hget",
|
123
|
+
"hmget",
|
124
|
+
"hexists",
|
125
|
+
"hlen",
|
126
|
+
"hkeys",
|
127
|
+
"hscan",
|
128
|
+
"hscan_each",
|
129
|
+
"hvals",
|
130
|
+
"hgetall",
|
131
|
+
"lindex",
|
132
|
+
"llen",
|
133
|
+
"lrange",
|
134
|
+
"mapped_hmget",
|
135
|
+
"mapped_mget",
|
136
|
+
"mget",
|
137
|
+
"persist",
|
138
|
+
"scard",
|
139
|
+
"scan",
|
140
|
+
"scan_each",
|
141
|
+
"sdiff",
|
142
|
+
"sismember",
|
143
|
+
"smembers",
|
144
|
+
"srandmember",
|
145
|
+
"sscan",
|
146
|
+
"sscan_each",
|
147
|
+
"strlen",
|
148
|
+
"sunion",
|
149
|
+
"type",
|
150
|
+
"zcard",
|
151
|
+
"zcount",
|
152
|
+
"zrange",
|
153
|
+
"zrangebyscore",
|
154
|
+
"zrank",
|
155
|
+
"zrevrange",
|
156
|
+
"zrevrangebyscore",
|
157
|
+
"zrevrank",
|
158
|
+
"zscan",
|
159
|
+
"zscan_each",
|
160
|
+
"zscore",
|
161
|
+
]
|
162
|
+
|
163
|
+
attr_reader :monitor_thread
|
164
|
+
attr_reader :initial_keys
|
165
|
+
attr_accessor :options
|
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
|
178
|
+
|
179
|
+
parse_message
|
180
|
+
end
|
181
|
+
|
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]
|
186
|
+
|
187
|
+
@cur_pos = end_pos + 2 # " ["
|
188
|
+
end_pos = @message.index(" ", @cur_pos)
|
189
|
+
@db = @message[@cur_pos..end_pos - 1].to_i
|
190
|
+
|
191
|
+
@cur_pos = end_pos + 1
|
192
|
+
end_pos = @message.index(":", @cur_pos)
|
193
|
+
@host = @message[@cur_pos..end_pos - 1]
|
194
|
+
|
195
|
+
@cur_pos = end_pos + 1
|
196
|
+
end_pos = @message.index("]", @cur_pos)
|
197
|
+
@port = @message[@cur_pos..end_pos - 1].to_i
|
198
|
+
|
199
|
+
@cur_pos = end_pos + 2 # "] "
|
200
|
+
@command = next_value.downcase
|
201
|
+
else
|
202
|
+
@command = @message
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
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)
|
213
|
+
|
214
|
+
num_backslashes = 0
|
215
|
+
back_pos = end_pos
|
216
|
+
|
217
|
+
while @message[back_pos - 1] == "\\"
|
218
|
+
num_backslashes += 1
|
219
|
+
back_pos -= 1
|
220
|
+
end
|
221
|
+
|
222
|
+
break if (num_backslashes % 2) == 0
|
223
|
+
end_pos += 1
|
224
|
+
end
|
225
|
+
else
|
226
|
+
end_pos = @message.index(" ", @cur_pos)
|
227
|
+
end
|
228
|
+
the_value = @message[@cur_pos..end_pos - 1]
|
229
|
+
end_pos += 1 if in_quote
|
230
|
+
|
231
|
+
@cur_pos = end_pos + 1
|
232
|
+
|
233
|
+
the_value.gsub("\\\\", "\\").gsub("\\\"", "\"")
|
234
|
+
end
|
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]
|
242
|
+
|
243
|
+
(before, after) = handling
|
244
|
+
|
245
|
+
case before
|
246
|
+
when :first
|
247
|
+
@message_keys << next_value
|
248
|
+
|
249
|
+
when :all
|
250
|
+
while @cur_pos < @message.length
|
251
|
+
@message_keys << next_value
|
252
|
+
end
|
253
|
+
|
254
|
+
when :exclude_first
|
255
|
+
next_value
|
256
|
+
while @cur_pos < @message.length
|
257
|
+
@message_keys << next_value
|
258
|
+
end
|
259
|
+
|
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
|
275
|
+
|
276
|
+
when :alternate
|
277
|
+
while @cur_pos < @message.length
|
278
|
+
@message_keys << next_value
|
279
|
+
next_value
|
280
|
+
end
|
281
|
+
|
282
|
+
when :sort
|
283
|
+
next_value
|
284
|
+
|
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
|
+
|
292
|
+
# when :eval_style
|
293
|
+
#
|
294
|
+
# when :scan_style
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
@message_keys
|
300
|
+
end
|
301
|
+
|
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
|
314
|
+
|
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
|
320
|
+
|
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}."
|
326
|
+
end
|
327
|
+
|
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
|
336
|
+
end
|
337
|
+
|
338
|
+
def <=>(right_object)
|
339
|
+
if (right_object.is_a?(PseudoCleaner::RedisMonitorCleaner))
|
340
|
+
return 0
|
341
|
+
elsif (right_object.is_a?(PseudoCleaner::TableCleaner))
|
342
|
+
return 1
|
343
|
+
else
|
344
|
+
if right_object.respond_to?(:<=>)
|
345
|
+
comparison = (right_object <=> self)
|
346
|
+
if comparison
|
347
|
+
return -1 * comparison
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
return 1
|
353
|
+
end
|
354
|
+
|
355
|
+
def redis
|
356
|
+
@redis ||= Redis.current
|
357
|
+
end
|
358
|
+
|
359
|
+
def suite_start test_strategy
|
360
|
+
@test_strategy ||= test_strategy
|
361
|
+
|
362
|
+
# if redis.type(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY) == "set"
|
363
|
+
# @initial_keys = SortedSet.new(redis.smembers(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY))
|
364
|
+
# report_end_of_suite_state "before suite start"
|
365
|
+
# end
|
366
|
+
# redis.del PseudoCleaner::RedisMonitorCleaner::SUITE_KEY
|
367
|
+
|
368
|
+
start_monitor
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_start test_strategy
|
372
|
+
@test_strategy ||= test_strategy
|
373
|
+
|
374
|
+
synchronize_test_values do |test_values|
|
375
|
+
if test_values && !test_values.empty?
|
376
|
+
report_dirty_values "values altered before the test started", test_values
|
377
|
+
|
378
|
+
test_values.each do |value|
|
379
|
+
redis.del value unless initial_keys.include?(value)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_end test_strategy
|
386
|
+
synchronize_test_values do |updated_values|
|
387
|
+
if updated_values && !updated_values.empty?
|
388
|
+
report_keys = []
|
389
|
+
|
390
|
+
if @options[:output_diagnostics]
|
391
|
+
report_dirty_values "updated values", updated_values
|
392
|
+
end
|
393
|
+
|
394
|
+
updated_values.each do |value|
|
395
|
+
if initial_keys.include?(value)
|
396
|
+
report_keys << value
|
397
|
+
@suite_altered_keys << value
|
398
|
+
else
|
399
|
+
redis.del(value)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
report_dirty_values "initial values altered by test", report_keys
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def suite_end test_strategy
|
409
|
+
report_end_of_suite_state "suite end"
|
410
|
+
|
411
|
+
if monitor_thread
|
412
|
+
monitor_thread.kill
|
413
|
+
@monitor_thread = nil
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def reset_suite
|
418
|
+
report_end_of_suite_state "reset suite"
|
419
|
+
|
420
|
+
if monitor_thread
|
421
|
+
monitor_thread.kill
|
422
|
+
@monitor_thread = nil
|
423
|
+
start_monitor
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def ignore_regexes
|
428
|
+
[]
|
429
|
+
end
|
430
|
+
|
431
|
+
def ignore_key(key)
|
432
|
+
ignore_regexes.detect { |ignore_regex| key =~ ignore_regex }
|
433
|
+
end
|
434
|
+
|
435
|
+
def redis_name
|
436
|
+
unless @redis_name
|
437
|
+
redis_options = redis.client.options.with_indifferent_access
|
438
|
+
@redis_name = "#{redis_options[:host]}:#{redis_options[:port]}/#{redis_options[:db]}"
|
439
|
+
end
|
440
|
+
|
441
|
+
@redis_name
|
442
|
+
end
|
443
|
+
|
444
|
+
def review_rows(&block)
|
445
|
+
synchronize_test_values do |updated_values|
|
446
|
+
if updated_values && !updated_values.empty?
|
447
|
+
updated_values.each do |updated_value|
|
448
|
+
unless ignore_key(updated_value)
|
449
|
+
block.yield redis_name, report_record(updated_value)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def peek_values
|
457
|
+
synchronize_test_values do |updated_values|
|
458
|
+
if updated_values && !updated_values.empty?
|
459
|
+
output_values = false
|
460
|
+
|
461
|
+
if PseudoCleaner::MasterCleaner.report_table
|
462
|
+
Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table,
|
463
|
+
nested_table_label: redis_name,
|
464
|
+
suppress_blank_table: true) do |report_table|
|
465
|
+
updated_values.each do |updated_value|
|
466
|
+
unless ignore_key(updated_value)
|
467
|
+
output_values = true
|
468
|
+
report_table.write_stats updated_value, report_record(updated_value)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
else
|
473
|
+
PseudoCleaner::Logger.write(" #{redis_name}")
|
474
|
+
|
475
|
+
updated_values.each do |updated_value|
|
476
|
+
unless ignore_key(updated_value)
|
477
|
+
output_values = true
|
478
|
+
PseudoCleaner::Logger.write(" #{updated_value}: #{report_record(updated_value)}")
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
PseudoCleaner::MasterCleaner.report_error if output_values
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
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
|
+
def report_end_of_suite_state report_reason
|
497
|
+
current_keys = SortedSet.new(redis.keys)
|
498
|
+
|
499
|
+
deleted_keys = initial_keys - current_keys
|
500
|
+
new_keys = current_keys - initial_keys
|
501
|
+
|
502
|
+
# filter out values we inserted that will go away on their own.
|
503
|
+
new_keys = new_keys.select { |key| (key =~ /redis_cleaner::synchronization_(?:end_)?key_[0-9]+_[0-9]+/).nil? }
|
504
|
+
|
505
|
+
report_dirty_values "new values as of #{report_reason}", new_keys
|
506
|
+
report_dirty_values "values deleted before #{report_reason}", deleted_keys
|
507
|
+
report_dirty_values "initial values changed during suite run", @suite_altered_keys
|
508
|
+
|
509
|
+
@suite_altered_keys = SortedSet.new
|
510
|
+
|
511
|
+
new_keys.each do |key_value|
|
512
|
+
redis.del key_value
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
def synchronize_test_values(&block)
|
517
|
+
updated_values = nil
|
518
|
+
|
519
|
+
if monitor_thread
|
520
|
+
redis.setex(synchronize_key, 1, true)
|
521
|
+
updated_values = queue.pop
|
522
|
+
end
|
523
|
+
|
524
|
+
block.yield updated_values
|
525
|
+
|
526
|
+
redis.setex(synchronize_end_key, 1, true)
|
527
|
+
end
|
528
|
+
|
529
|
+
def queue
|
530
|
+
@queue ||= Queue.new
|
531
|
+
end
|
532
|
+
|
533
|
+
def start_monitor
|
534
|
+
cleaner_class = self
|
535
|
+
|
536
|
+
@initial_keys = SortedSet.new(redis.keys)
|
537
|
+
# @initial_keys.add(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY)
|
538
|
+
# @initial_keys.each do |key_value|
|
539
|
+
# redis.sadd(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY, key_value)
|
540
|
+
# end
|
541
|
+
if @options[:output_diagnostics]
|
542
|
+
if PseudoCleaner::MasterCleaner.report_table
|
543
|
+
Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table,
|
544
|
+
nested_table_label: redis_name,
|
545
|
+
suppress_blank_table: true) do |report_table|
|
546
|
+
report_table.write_stats "initial keys count", @initial_keys.count
|
547
|
+
end
|
548
|
+
else
|
549
|
+
PseudoCleaner::Logger.write("#{redis_name}")
|
550
|
+
PseudoCleaner::Logger.write(" Initial keys count - #{@initial_keys.count}")
|
551
|
+
end
|
552
|
+
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::RedisMonitorCleaner::WRITE_COMMANDS.include? redis_message.command
|
591
|
+
updated_keys.merge(redis_message.keys)
|
592
|
+
elsif PseudoCleaner::RedisMonitorCleaner::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
|
+
end
|
608
|
+
|
609
|
+
def report_record(key_name)
|
610
|
+
key_hash = { key: key_name, type: redis.type(key_name), ttl: redis.ttl(key_name) }
|
611
|
+
case key_hash[:type]
|
612
|
+
when "string"
|
613
|
+
key_hash[:value] = redis.get(key_name)
|
614
|
+
when "list"
|
615
|
+
key_hash[:list] = { len: redis.llen(key_name), values: redis.lrange(key_name, 0, -1) }
|
616
|
+
when "set"
|
617
|
+
key_hash[:set] = redis.smembers(key_name)
|
618
|
+
when "zset"
|
619
|
+
key_hash[:sorted_set] = redis.smembers(key_name)
|
620
|
+
when "hash"
|
621
|
+
key_hash[:list] = { len: redis.hlen(key_name), values: redis.hgetall(key_name) }
|
622
|
+
end
|
623
|
+
|
624
|
+
if key_hash[:value].nil? &&
|
625
|
+
key_hash[:list].nil? &&
|
626
|
+
key_hash[:set].nil? &&
|
627
|
+
key_hash[:sorted_set].nil? &&
|
628
|
+
key_hash[:hash].nil?
|
629
|
+
key_hash[:value] = "[[DELETED]]"
|
630
|
+
end
|
631
|
+
|
632
|
+
key_hash
|
633
|
+
end
|
634
|
+
|
635
|
+
def report_dirty_values message, test_values
|
636
|
+
if test_values && !test_values.empty?
|
637
|
+
output_values = false
|
638
|
+
|
639
|
+
if PseudoCleaner::MasterCleaner.report_table
|
640
|
+
Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table,
|
641
|
+
nested_table_label: redis_name,
|
642
|
+
suppress_blank_table: true) do |report_table|
|
643
|
+
report_table.write_stats "action", message
|
644
|
+
test_values.each_with_index do |key_name, index|
|
645
|
+
unless ignore_key(key_name)
|
646
|
+
output_values = true
|
647
|
+
report_table.write_stats index, report_record(key_name)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
else
|
652
|
+
PseudoCleaner::Logger.write("********* RedisMonitorCleaner - #{message}".red.on_light_white)
|
653
|
+
test_values.each do |key_name|
|
654
|
+
unless ignore_key(key_name)
|
655
|
+
output_values = true
|
656
|
+
PseudoCleaner::Logger.write(" #{key_name}: #{report_record(key_name)}".red.on_light_white)
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
PseudoCleaner::MasterCleaner.report_error if output_values
|
662
|
+
end
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|