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