redistat 0.3.0 → 0.4.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.
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