metric_system 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/lib/metric_system/database.rb +164 -0
- data/lib/metric_system/io.rb +15 -0
- data/lib/metric_system/server.rb +126 -0
- data/lib/metric_system/sqlite3_extensions.rb +3 -157
- data/lib/metric_system/version.rb +2 -2
- data/lib/metric_system/web/dashboard.erb +163 -0
- data/lib/metric_system/web.rb +36 -0
- data/lib/metric_system.rb +24 -147
- data/lib/sqlite3/database_extension.rb +85 -0
- data/lib/sqlite3/query.rb +77 -0
- data/lib/sqlite3/record.rb +65 -0
- data/lib/to_js.rb +55 -0
- data/test/benchmark.rb +48 -0
- data/test/counters_test.rb +14 -9
- data/test/gauges_test.rb +9 -1
- data/test/mixed_test.rb +10 -2
- data/test/parallel.rb +68 -0
- metadata +13 -3
- data/lib/metric_system/benchmark.rb +0 -11
data/lib/metric_system.rb
CHANGED
@@ -1,67 +1,33 @@
|
|
1
1
|
require "expectation"
|
2
|
-
require_relative "metric_system/core_extensions"
|
3
|
-
require_relative "metric_system/sqlite3_extensions"
|
4
|
-
|
5
|
-
# -- The MetricSystem module --------------------------------------------------
|
6
2
|
|
3
|
+
require_relative "metric_system/core_extensions"
|
7
4
|
require "forwardable"
|
8
|
-
class MetricSystem
|
9
|
-
extend Forwardable
|
10
|
-
delegate [:exec, :select, :transaction, :rollback] => :@db
|
11
|
-
|
12
|
-
PERIODS = [
|
13
|
-
[ :year, 31536000, "strftime('%Y-01-01', starts_at, 'unixepoch')" ],
|
14
|
-
[ :month, 2592000, "strftime('%Y-%m-01', starts_at, 'unixepoch')" ],
|
15
|
-
[ :week, 604800, "strftime('%Y-%m-%d', starts_at, 'unixepoch', 'weekday 1', '-7 days')" ],
|
16
|
-
[ :day, 86400, "strftime('%Y-%m-%d', starts_at, 'unixepoch')" ],
|
17
|
-
[ :hour, 3600, "strftime('%Y-%m-%d %H:00:00', starts_at, 'unixepoch')" ],
|
18
|
-
[ :minute, 60, "strftime('%Y-%m-%d %H:%M:00', starts_at, 'unixepoch')" ],
|
19
|
-
[ :second, 1, "strftime('%Y-%m-%d %H:%M:%S', starts_at, 'unixepoch')" ],
|
20
|
-
]
|
21
5
|
|
22
|
-
|
23
|
-
|
6
|
+
module MetricSystem
|
7
|
+
extend self
|
24
8
|
|
25
|
-
|
26
|
-
exec <<-SQL
|
27
|
-
CREATE TABLE IF NOT EXISTS #{name}(
|
28
|
-
id INTEGER PRIMARY KEY,
|
9
|
+
attr :target, :database
|
29
10
|
|
30
|
-
|
31
|
-
|
32
|
-
starts_at TIMESTAMP NOT NULL DEFAULT (strftime('%s','now')) -- the timestamp
|
33
|
-
);
|
11
|
+
def target=(target)
|
12
|
+
@target = @database = nil
|
34
13
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
period, -- the name of the period (year, day, etc.)
|
44
|
-
sum, -- the sum of event values
|
45
|
-
count, -- the count of events
|
46
|
-
value -- the aggregated value
|
47
|
-
);
|
48
|
-
|
49
|
-
CREATE UNIQUE INDEX IF NOT EXISTS aggregated_#{name}_uidx1 ON aggregated_#{name}(name, starts_at, duration);
|
50
|
-
CREATE INDEX IF NOT EXISTS aggregated_#{name}_idx2 ON aggregated_#{name}(starts_at);
|
51
|
-
CREATE INDEX IF NOT EXISTS aggregated_#{name}_idx3 ON aggregated_#{name}(duration);
|
52
|
-
SQL
|
14
|
+
case target
|
15
|
+
when nil
|
16
|
+
when String
|
17
|
+
require_relative "metric_system/database"
|
18
|
+
@target = @database = MetricSystem::Database.new(target)
|
19
|
+
else
|
20
|
+
require_relative "metric_system/io"
|
21
|
+
@target = MetricSystem::IO.new(target)
|
53
22
|
end
|
54
|
-
|
55
|
-
exec <<-SQL
|
56
|
-
PRAGMA synchronous = NORMAL;
|
57
|
-
|
58
|
-
CREATE VIEW IF NOT EXISTS aggregates AS
|
59
|
-
SELECT * FROM aggregated_gauges
|
60
|
-
UNION
|
61
|
-
SELECT * FROM aggregated_counters
|
62
|
-
SQL
|
63
23
|
end
|
64
24
|
|
25
|
+
extend Forwardable
|
26
|
+
delegate [:aggregate, :select, :print, :run, :ask, :register] => :"@target"
|
27
|
+
delegate [:transaction] => :"@target"
|
28
|
+
delegate [:add_event] => :"@target"
|
29
|
+
delegate [:quit_server!] => :"@target"
|
30
|
+
|
65
31
|
def gauge(name, value, starts_at = nil)
|
66
32
|
add_event :gauges, name, value, starts_at
|
67
33
|
end
|
@@ -70,99 +36,10 @@ class MetricSystem
|
|
70
36
|
add_event :counters, name, value, starts_at
|
71
37
|
end
|
72
38
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
# 3 events: "a", "a.b", "a.b.c"
|
78
|
-
names = begin
|
79
|
-
parts = name.split(".")
|
80
|
-
parts.length.downto(1).map do |cnt|
|
81
|
-
parts[0,cnt].join(".")
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
if starts_at
|
86
|
-
starts_at = Time.parse(starts_at) if starts_at.is_a?(String)
|
87
|
-
|
88
|
-
names.each do |name|
|
89
|
-
@db.run "INSERT INTO #{table}(name, value, starts_at) VALUES(?, ?, ?)", name, value, starts_at.to_i
|
90
|
-
end
|
91
|
-
else
|
92
|
-
names.each do |name|
|
93
|
-
@db.run "INSERT INTO #{table}(name, value) VALUES(?, ?)", name, value
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
public
|
99
|
-
|
100
|
-
PERIODS_BY_KEY = PERIODS.by(&:first)
|
101
|
-
|
102
|
-
def aggregate(*keys)
|
103
|
-
if keys.empty?
|
104
|
-
keys = PERIODS.map(&:first)
|
105
|
-
end
|
106
|
-
|
107
|
-
transaction do
|
108
|
-
keys.each do |period|
|
109
|
-
aggregate_for_period :period => period, :source => :counters, :dest => :aggregated_counters, :aggregate => "sum"
|
110
|
-
end
|
111
|
-
|
112
|
-
@db.exec "DELETE FROM counters"
|
113
|
-
end
|
114
|
-
|
115
|
-
transaction do
|
116
|
-
keys.each do |period|
|
117
|
-
aggregate_for_period :period => period, :source => :gauges, :dest => :aggregated_gauges, :aggregate => "CAST(sum AS FLOAT) / count"
|
118
|
-
end
|
119
|
-
|
120
|
-
@db.exec "DELETE FROM gauges"
|
39
|
+
def measure(name, starts_at = nil, &block)
|
40
|
+
start = Time.now
|
41
|
+
yield.tap do
|
42
|
+
gauge name, Time.now - start, starts_at
|
121
43
|
end
|
122
44
|
end
|
123
|
-
|
124
|
-
def aggregate_for_period(options)
|
125
|
-
expect! options => {
|
126
|
-
:period => PERIODS.map(&:first)
|
127
|
-
}
|
128
|
-
period, source, dest, aggregate = options.values_at :period, :source, :dest, :aggregate
|
129
|
-
|
130
|
-
_, duration, starts_at = PERIODS_BY_KEY[period]
|
131
|
-
|
132
|
-
# sql expression to calculate value from sum and count of event values
|
133
|
-
aggregate = source == :gauges ? "CAST(sum AS FLOAT) / count" : "sum"
|
134
|
-
|
135
|
-
@db.exec <<-SQL
|
136
|
-
CREATE TEMPORARY TABLE batch AS
|
137
|
-
SELECT name, starts_at, SUM(sum) AS sum, SUM(count) AS count FROM
|
138
|
-
(
|
139
|
-
SELECT name AS name,
|
140
|
-
#{starts_at} AS starts_at,
|
141
|
-
SUM(value) AS sum,
|
142
|
-
COUNT(value) AS count
|
143
|
-
FROM #{source}
|
144
|
-
GROUP BY name, starts_at
|
145
|
-
|
146
|
-
UNION
|
147
|
-
|
148
|
-
SELECT #{dest}.name AS name,
|
149
|
-
#{dest}.starts_at AS starts_at,
|
150
|
-
sum AS sum,
|
151
|
-
count AS count
|
152
|
-
FROM #{dest}
|
153
|
-
INNER JOIN #{source} ON #{dest}.name=#{source}.name
|
154
|
-
WHERE duration=#{duration}
|
155
|
-
AND #{dest}.starts_at >= (SELECT MIN(starts_at) FROM #{source})
|
156
|
-
)
|
157
|
-
GROUP BY name, starts_at;
|
158
|
-
|
159
|
-
INSERT OR REPLACE INTO #{dest}(name, starts_at, period, duration, sum, count, value)
|
160
|
-
SELECT name, starts_at, '#{period}', #{duration}, sum, count, #{aggregate}
|
161
|
-
FROM batch;
|
162
|
-
SQL
|
163
|
-
|
164
|
-
@db.exec <<-SQL
|
165
|
-
DROP TABLE batch;
|
166
|
-
SQL
|
167
|
-
end
|
168
45
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class SQLite3::Database
|
2
|
+
# execute multiple SQL statements at once.
|
3
|
+
def exec(sql, *args)
|
4
|
+
args = prepare_arguments(args)
|
5
|
+
|
6
|
+
while sql =~ /\S/ do
|
7
|
+
statement = prepare(sql)
|
8
|
+
|
9
|
+
sql = statement.remainder
|
10
|
+
if statement.active?
|
11
|
+
statement.execute!(*args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue
|
16
|
+
STDERR.puts "#{sql}: #{$!}"
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
|
20
|
+
# -- cached queries ---------------------------------------------------------
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def query(sql)
|
25
|
+
expect! sql => [ String, Symbol ]
|
26
|
+
|
27
|
+
if sql.is_a?(Symbol)
|
28
|
+
expect! sql => registry.keys
|
29
|
+
sql = registry.fetch(sql)
|
30
|
+
end
|
31
|
+
|
32
|
+
@queries ||= {}
|
33
|
+
@queries[sql] ||= SQLite3::Query.new sql, prepare(sql)
|
34
|
+
end
|
35
|
+
|
36
|
+
def prepare_arguments(args)
|
37
|
+
args.map do |arg|
|
38
|
+
case arg
|
39
|
+
when Time then arg.to_i
|
40
|
+
when Date then arg.to_time.to_i
|
41
|
+
else arg
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
public
|
47
|
+
|
48
|
+
def run(sql, *args)
|
49
|
+
query(sql).run *prepare_arguments(args)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ask(sql, *args)
|
53
|
+
query(sql).ask *prepare_arguments(args)
|
54
|
+
end
|
55
|
+
|
56
|
+
# run a select like query. Returns an array of records.
|
57
|
+
def select(sql, *args)
|
58
|
+
query(sql).select *prepare_arguments(args)
|
59
|
+
end
|
60
|
+
|
61
|
+
def print(sql, *args)
|
62
|
+
require "pp"
|
63
|
+
|
64
|
+
results = select sql, *args
|
65
|
+
log_sql = sql.gsub(/\n/, " ").gsub(/\s+/, " ")
|
66
|
+
puts "=" * log_sql.length
|
67
|
+
puts log_sql
|
68
|
+
puts "-" * log_sql.length
|
69
|
+
|
70
|
+
results.each do |result|
|
71
|
+
pp result.to_a
|
72
|
+
end
|
73
|
+
puts "=" * log_sql.length
|
74
|
+
end
|
75
|
+
|
76
|
+
# -- query registry
|
77
|
+
|
78
|
+
def registry
|
79
|
+
@registry ||= {}
|
80
|
+
end
|
81
|
+
|
82
|
+
def register(name, query)
|
83
|
+
registry[name] = query
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class SQLite3::Query
|
2
|
+
def initialize(sql, statement)
|
3
|
+
expect! statement => SQLite3::Statement
|
4
|
+
|
5
|
+
@sql, @statement = sql, statement
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(*args)
|
9
|
+
# STDERR.puts "Q: #{@sql} #{args.map(&:inspect).join(", ")}"
|
10
|
+
@statement.execute *args
|
11
|
+
end
|
12
|
+
|
13
|
+
def select(*args)
|
14
|
+
@klass ||= SQLite3::Record.for_columns(@statement.columns)
|
15
|
+
|
16
|
+
ary = run(*args).map do |rec|
|
17
|
+
@klass.build *rec
|
18
|
+
end
|
19
|
+
|
20
|
+
ary.extend Description
|
21
|
+
ary.columns = @statement.columns
|
22
|
+
ary
|
23
|
+
end
|
24
|
+
|
25
|
+
module Description
|
26
|
+
attr :columns, true
|
27
|
+
|
28
|
+
# A Google Chart compatible data table; see
|
29
|
+
# https://developers.google.com/chart/interactive/docs/reference#dataparam
|
30
|
+
def data_table
|
31
|
+
cols = columns.map do |column|
|
32
|
+
type = case column
|
33
|
+
when /_at$/ then :datetime
|
34
|
+
when /_on$/ then :date
|
35
|
+
when /value/ then :number
|
36
|
+
else :string
|
37
|
+
end
|
38
|
+
|
39
|
+
{ id: column, type: type, label: column }
|
40
|
+
end
|
41
|
+
|
42
|
+
rows = map { |record| convert_record record, cols }
|
43
|
+
|
44
|
+
{ cols: cols, rows: rows }
|
45
|
+
end
|
46
|
+
|
47
|
+
def convert_record(record, cols)
|
48
|
+
values = cols.map do |col|
|
49
|
+
id, type = col.values_at(:id, :type)
|
50
|
+
v = record.send(id)
|
51
|
+
|
52
|
+
case type
|
53
|
+
when :date then f = v.strftime("%a %b, %Y")
|
54
|
+
when :datetime then f = v.inspect
|
55
|
+
when :number then f = v
|
56
|
+
else f = v
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
{ v: v, f: f }
|
61
|
+
end
|
62
|
+
|
63
|
+
{ c: values }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def ask(*args)
|
68
|
+
results = run(*args)
|
69
|
+
row = results.first
|
70
|
+
results.reset
|
71
|
+
|
72
|
+
if !row then nil
|
73
|
+
elsif row.length == 1 then row.first
|
74
|
+
else row
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# manage SQLite3::Records
|
2
|
+
#
|
3
|
+
# The SQLite3::Record module is able to generate classes that are optimized
|
4
|
+
# for a specific set of columns. It is build on top of Struct, which is way
|
5
|
+
# faster than Hashes, for example.
|
6
|
+
module SQLite3::Record
|
7
|
+
module ClassMethods
|
8
|
+
attr :columns, true
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def to_time(s)
|
13
|
+
case s
|
14
|
+
when String then Time.parse(s)
|
15
|
+
when Fixnum then Time.at(s)
|
16
|
+
else s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_date(s)
|
21
|
+
return unless time = to_time(s)
|
22
|
+
time.to_date
|
23
|
+
end
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
def build(*attrs)
|
28
|
+
attrs = columns.zip(attrs).map do |key, value|
|
29
|
+
case key
|
30
|
+
when /_at$/ then to_time(value)
|
31
|
+
when /_on$/ then to_date(value)
|
32
|
+
else value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
new *attrs
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_a
|
41
|
+
self.class.columns.map do |column| send(column) end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
kvs = self.class.columns.inject([]) do |ary, column|
|
46
|
+
ary << column << send(column)
|
47
|
+
end
|
48
|
+
|
49
|
+
Hash[*kvs]
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.for_columns(columns)
|
53
|
+
columns = columns.map(&:to_sym)
|
54
|
+
|
55
|
+
@@classes ||= {}
|
56
|
+
@@classes[columns] ||= begin
|
57
|
+
struct = Struct.new(*columns)
|
58
|
+
struct.extend SQLite3::Record::ClassMethods
|
59
|
+
struct.include SQLite3::Record
|
60
|
+
|
61
|
+
struct.columns = columns
|
62
|
+
struct
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/to_js.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
class Object
|
4
|
+
def to_js
|
5
|
+
convert_to_js
|
6
|
+
end
|
7
|
+
|
8
|
+
alias :convert_to_js :to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
class Array
|
12
|
+
def convert_to_js
|
13
|
+
"[" + map(&:convert_to_js).join(", ") + "]"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Hash
|
18
|
+
def convert_to_js
|
19
|
+
"{" + map { |k,v| "#{k.convert_to_js}: #{v.convert_to_js}" }.join(", ") + "}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Date
|
24
|
+
def convert_to_js
|
25
|
+
"new Date(#{year}, #{month-1}, #{day})"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Time
|
30
|
+
def convert_to_js
|
31
|
+
"new Date(#{year}, #{month-1}, #{day}, #{hour}, #{min}, #{sec}, #{usec / 1000})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Numeric
|
36
|
+
def convert_to_js
|
37
|
+
"%f" % self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class OpenStruct
|
42
|
+
def convert_to_js
|
43
|
+
@table.to_js
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if defined?(SQLite3::Record)
|
48
|
+
|
49
|
+
module SQLite3::Record::ClassMethods
|
50
|
+
def convert_to_js
|
51
|
+
to_hash.convert_to_js
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/test/benchmark.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
$: << "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
require "metric_system"
|
3
|
+
|
4
|
+
def benchmark(msg, &block)
|
5
|
+
starts = Time.now
|
6
|
+
|
7
|
+
yield
|
8
|
+
|
9
|
+
ensure
|
10
|
+
runtime = Time.now - starts
|
11
|
+
if runtime > 0.1
|
12
|
+
STDERR.puts "%s: %.3f secs" % [ msg, runtime = Time.now - starts ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
TICKS_PER_DAY = 86400
|
17
|
+
TICKS_PER_DAY = 10
|
18
|
+
DBPATH = "samples.sqlite"
|
19
|
+
|
20
|
+
File.unlink(DBPATH) rescue nil
|
21
|
+
MetricSystem.target = DBPATH
|
22
|
+
|
23
|
+
benchmark "Building metric_system" do
|
24
|
+
distance = 24 * 3600.0 / TICKS_PER_DAY
|
25
|
+
|
26
|
+
0.upto(365) do |day|
|
27
|
+
midnight = Time.parse("2013-01-01").to_i + day * 24 * 3600
|
28
|
+
|
29
|
+
benchmark "Day ##{day}: add metrics" do
|
30
|
+
MetricSystem.transaction do
|
31
|
+
1.upto(TICKS_PER_DAY) do |step|
|
32
|
+
time = midnight + distance * step
|
33
|
+
STDERR.print "."
|
34
|
+
MetricSystem.count "clicks", 1, time
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
benchmark "Day ##{day}: aggregate" do
|
40
|
+
MetricSystem.aggregate
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
MetricSystem.print "SELECT COUNT(*) FROM aggregates"
|
46
|
+
|
47
|
+
__END__
|
48
|
+
db.print "SELECT * FROM aggregates"
|
data/test/counters_test.rb
CHANGED
@@ -3,8 +3,16 @@ require "metric_system"
|
|
3
3
|
require "test/unit"
|
4
4
|
|
5
5
|
class MetricSystem::TestCounters < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
MetricSystem.target = ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
MetricSystem.target = nil
|
12
|
+
end
|
13
|
+
|
6
14
|
def db
|
7
|
-
|
15
|
+
MetricSystem
|
8
16
|
end
|
9
17
|
|
10
18
|
def test_two_events
|
@@ -25,6 +33,8 @@ class MetricSystem::TestCounters < Test::Unit::TestCase
|
|
25
33
|
end
|
26
34
|
|
27
35
|
def test_conversion
|
36
|
+
db = MetricSystem.target
|
37
|
+
expect! db => MetricSystem::Database
|
28
38
|
db.exec "CREATE TABLE tmp(value, starts_at, starts_on)"
|
29
39
|
now = Time.parse("2014-03-02 11:10:11 +0100")
|
30
40
|
day = Date.parse("2014-03-02")
|
@@ -42,7 +52,6 @@ class MetricSystem::TestCounters < Test::Unit::TestCase
|
|
42
52
|
|
43
53
|
r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name")
|
44
54
|
assert_equal(r.map(&:to_a), [
|
45
|
-
["foo", 1, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
46
55
|
["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
47
56
|
["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
48
57
|
["foo", 1, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
@@ -66,7 +75,7 @@ class MetricSystem::TestCounters < Test::Unit::TestCase
|
|
66
75
|
def test_combined_name
|
67
76
|
db.count "foo", 3, "2014-03-02 12:10:11"
|
68
77
|
db.count "foo.bar", 2, "2014-03-02 12:10:11"
|
69
|
-
db.aggregate :
|
78
|
+
db.aggregate :minute, :hour
|
70
79
|
|
71
80
|
r = db.select <<-SQL
|
72
81
|
SELECT name, value, period, starts_at
|
@@ -75,8 +84,6 @@ class MetricSystem::TestCounters < Test::Unit::TestCase
|
|
75
84
|
SQL
|
76
85
|
|
77
86
|
assert_equal(r.map(&:to_a), [
|
78
|
-
["foo" , 5, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
79
|
-
["foo.bar", 2, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
80
87
|
["foo" , 5, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
81
88
|
["foo.bar", 2, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
82
89
|
["foo" , 5, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
@@ -87,8 +94,8 @@ class MetricSystem::TestCounters < Test::Unit::TestCase
|
|
87
94
|
def test_double_aggregate
|
88
95
|
db.count "foo", 3, "2014-03-02 12:10:11"
|
89
96
|
db.count "foo.bar", 2, "2014-03-02 12:10:11"
|
90
|
-
db.aggregate :
|
91
|
-
db.aggregate :
|
97
|
+
db.aggregate :minute, :hour
|
98
|
+
db.aggregate :minute, :hour
|
92
99
|
|
93
100
|
r = db.select <<-SQL
|
94
101
|
SELECT name, value, period, starts_at
|
@@ -97,8 +104,6 @@ class MetricSystem::TestCounters < Test::Unit::TestCase
|
|
97
104
|
SQL
|
98
105
|
|
99
106
|
assert_equal(r.map(&:to_a), [
|
100
|
-
["foo" , 5, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
101
|
-
["foo.bar", 2, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
102
107
|
["foo" , 5, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
103
108
|
["foo.bar", 2, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
104
109
|
["foo" , 5, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
data/test/gauges_test.rb
CHANGED
@@ -3,8 +3,16 @@ require "metric_system"
|
|
3
3
|
require "test/unit"
|
4
4
|
|
5
5
|
class MetricSystem::TestGauging < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
MetricSystem.target = ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
MetricSystem.target = nil
|
12
|
+
end
|
13
|
+
|
6
14
|
def db
|
7
|
-
|
15
|
+
MetricSystem
|
8
16
|
end
|
9
17
|
|
10
18
|
def test_two_events
|
data/test/mixed_test.rb
CHANGED
@@ -3,11 +3,19 @@ require "metric_system"
|
|
3
3
|
require "test/unit"
|
4
4
|
|
5
5
|
class MetricSystem::TestMixed < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
MetricSystem.target = ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
MetricSystem.target = nil
|
12
|
+
end
|
13
|
+
|
6
14
|
def db
|
7
|
-
|
15
|
+
MetricSystem
|
8
16
|
end
|
9
17
|
|
10
|
-
def
|
18
|
+
def test_two_events
|
11
19
|
db.gauge "foo", 1, "2014-03-02 12:10:11"
|
12
20
|
db.gauge "foo", 2, "2014-03-02 14:10:11"
|
13
21
|
db.count "bar", 1, "2014-03-02 12:10:11"
|
data/test/parallel.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
$: << "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
require "metric_system"
|
3
|
+
|
4
|
+
SOCKET = "performance.socket"
|
5
|
+
DBPATH = "samples.sqlite"
|
6
|
+
|
7
|
+
mode, _ = *ARGV
|
8
|
+
if mode == nil then
|
9
|
+
require "metric_system/server"
|
10
|
+
|
11
|
+
Thread.new do
|
12
|
+
sleep 1
|
13
|
+
system "ruby #{__FILE__} sender"
|
14
|
+
end
|
15
|
+
|
16
|
+
MetricSystem::Server.run DBPATH, SOCKET, :quit_server => true
|
17
|
+
|
18
|
+
puts "server stopped"
|
19
|
+
|
20
|
+
MetricSystem.print "SELECT COUNT(*) FROM aggregates"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
# ---------------------------------------------------------------------
|
25
|
+
|
26
|
+
def benchmark(msg, &block)
|
27
|
+
starts = Time.now
|
28
|
+
|
29
|
+
yield
|
30
|
+
|
31
|
+
ensure
|
32
|
+
runtime = Time.now - starts
|
33
|
+
if runtime > 0.1
|
34
|
+
STDERR.puts "%s: %.3f secs" % [ msg, runtime = Time.now - starts ]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# TICKS_PER_DAY = 86400
|
39
|
+
TICKS_PER_DAY = 10
|
40
|
+
|
41
|
+
File.unlink(DBPATH) rescue nil
|
42
|
+
|
43
|
+
require "time"
|
44
|
+
require "socket"
|
45
|
+
|
46
|
+
benchmark "Sending #{365 * TICKS_PER_DAY} events to metric_system" do
|
47
|
+
distance = 24 * 3600.0 / TICKS_PER_DAY
|
48
|
+
MetricSystem.target = UNIXSocket.new(SOCKET)
|
49
|
+
0.upto(364) do |day|
|
50
|
+
midnight = Time.parse("2013-01-01").to_i + day * 24 * 3600
|
51
|
+
|
52
|
+
benchmark "Day ##{day}: add metrics" do
|
53
|
+
1.upto(TICKS_PER_DAY) do |step|
|
54
|
+
time = midnight + distance * step
|
55
|
+
MetricSystem.count "clicks", 1, time
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
STDERR.puts "Quitting server"
|
62
|
+
MetricSystem.quit_server!
|
63
|
+
|
64
|
+
__END__
|
65
|
+
|
66
|
+
db.print "SELECT COUNT(*) FROM aggregates"
|
67
|
+
|
68
|
+
db.print "SELECT * FROM aggregates"
|