redistat 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.travis.yml +8 -0
  2. data/README.md +219 -97
  3. data/lib/redistat.rb +13 -13
  4. data/lib/redistat/buffer.rb +27 -24
  5. data/lib/redistat/collection.rb +5 -5
  6. data/lib/redistat/connection.rb +23 -18
  7. data/lib/redistat/core_ext.rb +1 -1
  8. data/lib/redistat/core_ext/bignum.rb +2 -2
  9. data/lib/redistat/core_ext/date.rb +2 -2
  10. data/lib/redistat/core_ext/fixnum.rb +2 -2
  11. data/lib/redistat/core_ext/hash.rb +4 -4
  12. data/lib/redistat/date.rb +11 -11
  13. data/lib/redistat/event.rb +18 -18
  14. data/lib/redistat/finder.rb +39 -39
  15. data/lib/redistat/finder/date_set.rb +4 -4
  16. data/lib/redistat/key.rb +16 -16
  17. data/lib/redistat/label.rb +14 -14
  18. data/lib/redistat/mixins/database.rb +1 -1
  19. data/lib/redistat/mixins/date_helper.rb +1 -1
  20. data/lib/redistat/mixins/options.rb +8 -8
  21. data/lib/redistat/mixins/synchronize.rb +12 -11
  22. data/lib/redistat/model.rb +25 -17
  23. data/lib/redistat/result.rb +4 -4
  24. data/lib/redistat/scope.rb +5 -5
  25. data/lib/redistat/summary.rb +33 -26
  26. data/lib/redistat/version.rb +1 -1
  27. data/redistat.gemspec +4 -3
  28. data/spec/buffer_spec.rb +27 -25
  29. data/spec/collection_spec.rb +4 -4
  30. data/spec/connection_spec.rb +12 -12
  31. data/spec/core_ext/hash_spec.rb +5 -5
  32. data/spec/database_spec.rb +3 -3
  33. data/spec/date_spec.rb +15 -15
  34. data/spec/event_spec.rb +8 -8
  35. data/spec/finder/date_set_spec.rb +134 -134
  36. data/spec/finder_spec.rb +36 -36
  37. data/spec/key_spec.rb +19 -19
  38. data/spec/label_spec.rb +10 -10
  39. data/spec/model_helper.rb +10 -9
  40. data/spec/model_spec.rb +38 -41
  41. data/spec/options_spec.rb +9 -9
  42. data/spec/result_spec.rb +4 -4
  43. data/spec/scope_spec.rb +6 -6
  44. data/spec/spec_helper.rb +6 -0
  45. data/spec/summary_spec.rb +31 -24
  46. data/spec/synchronize_spec.rb +118 -57
  47. data/spec/thread_safety_spec.rb +6 -6
  48. metadata +88 -126
  49. data/.rvmrc +0 -1
@@ -3,33 +3,33 @@ require 'redistat/core_ext/hash'
3
3
  module Redistat
4
4
  class Buffer
5
5
  include Synchronize
6
-
6
+
7
7
  def self.instance
8
8
  @instance ||= self.new
9
9
  end
10
-
10
+
11
11
  def size
12
12
  synchronize do
13
13
  @size ||= 0
14
14
  end
15
15
  end
16
-
16
+
17
17
  def size=(value)
18
18
  synchronize do
19
19
  @size = value
20
20
  end
21
21
  end
22
-
22
+
23
23
  def count
24
24
  @count ||= 0
25
25
  end
26
-
26
+
27
27
  def store(key, stats, depth_limit, opts)
28
28
  return false unless should_buffer?
29
-
29
+
30
30
  to_flush = {}
31
31
  buffkey = buffer_key(key, opts)
32
-
32
+
33
33
  synchronize do
34
34
  if !queue.has_key?(buffkey)
35
35
  queue[buffkey] = { :key => key,
@@ -37,19 +37,19 @@ module Redistat
37
37
  :depth_limit => depth_limit,
38
38
  :opts => opts }
39
39
  end
40
-
40
+
41
41
  queue[buffkey][:stats].merge_and_incr!(stats)
42
42
  incr_count
43
-
43
+
44
44
  # return items to be flushed if buffer size limit has been reached
45
45
  to_flush = reset_queue
46
46
  end
47
-
47
+
48
48
  # flush any data that's been cleared from the queue
49
49
  flush_data(to_flush)
50
50
  true
51
51
  end
52
-
52
+
53
53
  def flush(force = false)
54
54
  to_flush = {}
55
55
  synchronize do
@@ -57,28 +57,28 @@ module Redistat
57
57
  end
58
58
  flush_data(to_flush)
59
59
  end
60
-
60
+
61
61
  private
62
-
62
+
63
63
  # should always be called from within a synchronize block
64
64
  def incr_count
65
65
  @count ||= 0
66
66
  @count += 1
67
67
  end
68
-
68
+
69
69
  def queue
70
70
  @queue ||= {}
71
71
  end
72
-
72
+
73
73
  def should_buffer?
74
74
  size > 1 # buffer size of 1 would be equal to not using buffer
75
75
  end
76
-
76
+
77
77
  # should always be called from within a synchronize block
78
78
  def should_flush?
79
79
  (!queue.blank? && count >= size)
80
80
  end
81
-
81
+
82
82
  # returns items to be flushed if buffer size limit has been reached
83
83
  # should always be called from within a synchronize block
84
84
  def reset_queue(force = false)
@@ -88,20 +88,23 @@ module Redistat
88
88
  @count = 0
89
89
  data
90
90
  end
91
-
91
+
92
92
  def flush_data(buffer_data)
93
93
  buffer_data.each do |k, item|
94
94
  Summary.update(item[:key], item[:stats], item[:depth_limit], item[:opts])
95
95
  end
96
96
  end
97
-
97
+
98
+ # depth_limit is not needed as it's evident in key.to_s
98
99
  def buffer_key(key, opts)
99
- # depth_limit is not needed as it's evident in key.to_s
100
- opts_index = Summary.default_options.keys.sort { |a,b| a.to_s <=> b.to_s }.map do |k|
101
- opts[k] if opts.has_key?(k)
100
+ # covert keys to strings, as sorting a Hash with Symbol keys fails on
101
+ # Ruby 1.8.x.
102
+ opts = opts.inject({}) do |result, (k, v)|
103
+ result[k.to_s] = v
104
+ result
102
105
  end
103
- "#{key.to_s}:#{opts_index.join(':')}"
106
+ "#{key.to_s}:#{opts.sort.flatten.join(':')}"
104
107
  end
105
-
108
+
106
109
  end
107
110
  end
@@ -1,20 +1,20 @@
1
1
  module Redistat
2
2
  class Collection < ::Array
3
-
3
+
4
4
  attr_accessor :from
5
5
  attr_accessor :till
6
6
  attr_accessor :depth
7
7
  attr_accessor :total
8
-
8
+
9
9
  def initialize(options = {})
10
10
  @from = options[:from] ||= nil
11
11
  @till = options[:till] ||= nil
12
12
  @depth = options[:depth] ||= nil
13
13
  end
14
-
14
+
15
15
  def total
16
16
  @total ||= {}
17
17
  end
18
-
18
+
19
19
  end
20
- end
20
+ end
@@ -2,22 +2,23 @@ require 'monitor'
2
2
 
3
3
  module Redistat
4
4
  module Connection
5
-
5
+
6
6
  REQUIRED_SERVER_VERSION = "1.3.10"
7
-
7
+ MIN_EXPIRE_SERVER_VERSION = "2.1.3"
8
+
8
9
  # TODO: Create a ConnectionPool instance object using Sychronize mixin to replace Connection class
9
-
10
+
10
11
  class << self
11
-
12
+
12
13
  # TODO: clean/remove all ref-less connections
13
-
14
+
14
15
  def get(ref = nil)
15
16
  ref ||= :default
16
17
  synchronize do
17
18
  connections[references[ref]] || create
18
19
  end
19
20
  end
20
-
21
+
21
22
  def add(conn, ref = nil)
22
23
  ref ||= :default
23
24
  synchronize do
@@ -26,7 +27,7 @@ module Redistat
26
27
  connections[conn.client.id] = conn
27
28
  end
28
29
  end
29
-
30
+
30
31
  def create(options = {})
31
32
  synchronize do
32
33
  options = options.clone
@@ -37,39 +38,43 @@ module Redistat
37
38
  conn
38
39
  end
39
40
  end
40
-
41
+
41
42
  def connections
42
43
  @connections ||= {}
43
44
  end
44
-
45
+
45
46
  def references
46
47
  @references ||= {}
47
48
  end
48
-
49
+
49
50
  private
50
-
51
+
51
52
  def monitor
52
53
  @monitor ||= Monitor.new
53
54
  end
54
-
55
+
55
56
  def synchronize(&block)
56
57
  monitor.synchronize(&block)
57
58
  end
58
-
59
+
59
60
  def connection(options)
60
61
  check_redis_version(Redis.new(options))
61
62
  end
62
-
63
+
63
64
  def connection_id(options = {})
64
65
  options = options.reverse_merge(default_options)
65
66
  "redis://#{options[:host]}:#{options[:port]}/#{options[:db]}"
66
67
  end
67
-
68
+
68
69
  def check_redis_version(conn)
69
70
  raise RedisServerIsTooOld if conn.info["redis_version"] < REQUIRED_SERVER_VERSION
71
+ if conn.info["redis_version"] < MIN_EXPIRE_SERVER_VERSION
72
+ STDOUT.puts "WARNING: You MUST upgrade Redis to v2.1.3 or later " +
73
+ "if you are using key expiry."
74
+ end
70
75
  conn
71
76
  end
72
-
77
+
73
78
  def default_options
74
79
  {
75
80
  :host => '127.0.0.1',
@@ -78,7 +83,7 @@ module Redistat
78
83
  :timeout => 5
79
84
  }
80
85
  end
81
-
86
+
82
87
  end
83
88
  end
84
- end
89
+ end
@@ -2,4 +2,4 @@ require 'redistat/core_ext/bignum'
2
2
  require 'redistat/core_ext/date'
3
3
  require 'redistat/core_ext/fixnum'
4
4
  require 'redistat/core_ext/hash'
5
- require 'redistat/core_ext/time'
5
+ require 'redistat/core_ext/time'
@@ -1,8 +1,8 @@
1
1
  class Bignum
2
2
  include Redistat::DateHelper
3
-
3
+
4
4
  def to_time
5
5
  Time.at(self)
6
6
  end
7
-
7
+
8
8
  end
@@ -1,8 +1,8 @@
1
1
  class Date
2
2
  include Redistat::DateHelper
3
-
3
+
4
4
  def to_time
5
5
  Time.parse(self.to_s)
6
6
  end
7
-
7
+
8
8
  end
@@ -1,8 +1,8 @@
1
1
  class Fixnum
2
2
  include Redistat::DateHelper
3
-
3
+
4
4
  def to_time
5
5
  Time.at(self)
6
6
  end
7
-
7
+
8
8
  end
@@ -1,9 +1,9 @@
1
1
  class Hash
2
-
2
+
3
3
  def merge_and_incr(hash)
4
4
  self.clone.merge_and_incr!(hash)
5
5
  end
6
-
6
+
7
7
  def merge_and_incr!(hash)
8
8
  raise ArgumentError unless hash.is_a?(Hash)
9
9
  hash.each do |key, value|
@@ -11,7 +11,7 @@ class Hash
11
11
  end
12
12
  self
13
13
  end
14
-
14
+
15
15
  def set_or_incr(key, value)
16
16
  return false unless value.is_a?(Numeric)
17
17
  self[key] = 0 unless self.has_key?(key)
@@ -19,5 +19,5 @@ class Hash
19
19
  self[key] += value
20
20
  true
21
21
  end
22
-
22
+
23
23
  end
@@ -1,6 +1,6 @@
1
1
  module Redistat
2
2
  class Date
3
-
3
+
4
4
  attr_accessor :year
5
5
  attr_accessor :month
6
6
  attr_accessor :day
@@ -9,9 +9,9 @@ module Redistat
9
9
  attr_accessor :sec
10
10
  attr_accessor :usec
11
11
  attr_accessor :depth
12
-
12
+
13
13
  DEPTHS = [:year, :month, :day, :hour, :min, :sec, :usec]
14
-
14
+
15
15
  def initialize(input, depth = nil)
16
16
  @depth = depth
17
17
  if input.is_a?(::Time)
@@ -26,12 +26,12 @@ module Redistat
26
26
  from_integer(input)
27
27
  end
28
28
  end
29
-
29
+
30
30
  def to_t
31
31
  ::Time.local(@year, @month, @day, @hour, @min, @sec, @usec)
32
32
  end
33
33
  alias :to_time :to_t
34
-
34
+
35
35
  def to_d
36
36
  ::Date.civil(@year, @month, @day)
37
37
  end
@@ -41,7 +41,7 @@ module Redistat
41
41
  to_time.to_i
42
42
  end
43
43
  alias :to_integer :to_i
44
-
44
+
45
45
  def to_s(depth = nil)
46
46
  depth ||= @depth ||= :sec
47
47
  output = ""
@@ -57,9 +57,9 @@ module Redistat
57
57
  output
58
58
  end
59
59
  alias :to_string :to_s
60
-
60
+
61
61
  private
62
-
62
+
63
63
  def from_time(input)
64
64
  DEPTHS.each do |k|
65
65
  send("#{k}=", input.send(k))
@@ -74,15 +74,15 @@ module Redistat
74
74
  send("#{k}=", 0)
75
75
  end
76
76
  end
77
-
77
+
78
78
  def from_integer(input)
79
79
  from_time(::Time.at(input))
80
80
  end
81
-
81
+
82
82
  def from_string(input)
83
83
  input += "19700101000000"[input.size..-1] if input =~ /^\d\d\d[\d]+$/i
84
84
  from_time(::Time.parse(input))
85
85
  end
86
-
86
+
87
87
  end
88
88
  end
@@ -2,13 +2,13 @@ module Redistat
2
2
  class Event
3
3
  include Database
4
4
  include Options
5
-
5
+
6
6
  attr_reader :id
7
7
  attr_reader :key
8
-
8
+
9
9
  attr_accessor :stats
10
10
  attr_accessor :meta
11
-
11
+
12
12
  def default_options
13
13
  { :depth => :hour,
14
14
  :store_event => false,
@@ -16,7 +16,7 @@ module Redistat
16
16
  :enable_grouping => true,
17
17
  :label_indexing => true }
18
18
  end
19
-
19
+
20
20
  def initialize(scope, label = nil, date = nil, stats = {}, opts = {}, meta = {}, is_new = true)
21
21
  parse_options(opts)
22
22
  @key = Key.new(scope, label, date, @options)
@@ -24,35 +24,35 @@ module Redistat
24
24
  @meta = meta ||= {}
25
25
  @new = is_new
26
26
  end
27
-
27
+
28
28
  def new?
29
29
  @new
30
30
  end
31
-
31
+
32
32
  def date
33
33
  @key.date
34
34
  end
35
-
35
+
36
36
  def date=(input)
37
37
  @key.date = input
38
38
  end
39
-
39
+
40
40
  def scope
41
41
  @key.scope
42
42
  end
43
-
43
+
44
44
  def scope=(input)
45
45
  @key.scope = input
46
46
  end
47
-
47
+
48
48
  def label
49
49
  @key.label
50
50
  end
51
-
51
+
52
52
  def label_hash
53
53
  @key.label_hash
54
54
  end
55
-
55
+
56
56
  def label=(input)
57
57
  @key.label = input
58
58
  end
@@ -60,7 +60,7 @@ module Redistat
60
60
  def next_id
61
61
  db.incr("#{self.scope}#{KEY_NEXT_ID}")
62
62
  end
63
-
63
+
64
64
  def save
65
65
  return false if !self.new?
66
66
  Summary.update_all(@key, @stats, depth_limit, @options)
@@ -78,21 +78,21 @@ module Redistat
78
78
  @new = false
79
79
  self
80
80
  end
81
-
81
+
82
82
  def depth_limit
83
83
  @options[:depth] ||= @key.depth
84
84
  end
85
-
85
+
86
86
  def self.create(*args)
87
87
  self.new(*args).save
88
88
  end
89
-
89
+
90
90
  def self.find(scope, id)
91
91
  event = db.hgetall "#{scope}#{KEY_EVENT}#{id}"
92
92
  return nil if event.size == 0
93
93
  self.new( event["scope"], event["label"], event["date"], JSON.parse(event["stats"]),
94
94
  JSON.parse(event["options"]), JSON.parse(event["meta"]), false )
95
95
  end
96
-
96
+
97
97
  end
98
- end
98
+ end