quark 0.0.1

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.
data/bin/quark ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'quark'
4
+ require 'quark/config'
5
+
6
+ module Quark
7
+ class CLI
8
+ class<<self
9
+ def run()
10
+ @options = {}
11
+
12
+ (OptionParser.new do |opts|
13
+ opts.banner = "Usage: quark [options]"
14
+
15
+ # -----------------------------------------------------------------------------
16
+ opts.on('-H', '--redis-host HOST', 'Hostname of the Redis server to write to') do |host|
17
+ Quark::Config.set("redis.host", host)
18
+ end
19
+
20
+ # -----------------------------------------------------------------------------
21
+ opts.on('-P', '--redis-port PORT', 'Hostname of the Redis server to write to') do |port|
22
+ Quark::Config.set("redis.port", port)
23
+ end
24
+
25
+ # -----------------------------------------------------------------------------
26
+ opts.on('-N', '--redis-prefix PREFIX', 'The string used to prefix keys in Redis') do |prefix|
27
+ Quark::Config.set("redis.prefix", prefix)
28
+ end
29
+
30
+ # -----------------------------------------------------------------------------
31
+ opts.on('-S', '--socket FILE', 'Location of the Unix socket to accept commands from') do |file|
32
+ Quark::Config.set("quark.socket", file)
33
+ end
34
+
35
+ # -----------------------------------------------------------------------------
36
+ opts.on('-a', '--listen-address ADDR', 'The IP address to listen on') do |addr|
37
+ Quark::Config.set("quark.address", addr)
38
+ end
39
+
40
+ # -----------------------------------------------------------------------------
41
+ opts.on('-p', '--listen-port PORT', 'The TCP port to listen on') do |port|
42
+ Quark::Config.set("quark.port", port)
43
+ end
44
+
45
+ end).parse!
46
+
47
+ Quark::Server.run()
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ Quark::CLI.run()
data/lib/quark.rb ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ require 'eventmachine'
3
+ require 'redis'
4
+ require 'hiredis'
5
+ require 'em-synchrony'
6
+ require 'multi_json'
7
+ require 'quark/config'
8
+
9
+
10
+ module Quark
11
+ module SocketServer
12
+ Dir[File.join(File.dirname(__FILE__), "quark", "commands", "*.rb")].each do |rb|
13
+ require "#{rb.gsub(/\.rb$/,'')}"
14
+ end
15
+
16
+ def post_init()
17
+ @redis = Redis.new({
18
+ :host => Quark::Config.get("redis.host", "127.0.0.1"),
19
+ :port => Quark::Config.get("redis.port", 6379).to_i,
20
+ :db => Quark::Config.get("redis.db", 0).to_i
21
+ })
22
+
23
+ @_prefix = Quark::Config.get("redis.prefix", "quark")
24
+ end
25
+
26
+ def receive_data(data)
27
+ command, arguments = data.chomp.split(' ',2)
28
+
29
+ if methods.include?(:"process_command_#{command.downcase}")
30
+ rv = send(:"process_command_#{command.downcase}", arguments)
31
+ if rv.nil?
32
+ send_data(MultiJson.dump({
33
+ :success => true
34
+ })+"\n")
35
+ else
36
+ send_data(MultiJson.dump({
37
+ :success => true,
38
+ :command => data.chomp,
39
+ :results => rv
40
+ })+"\n")
41
+ end
42
+ end
43
+ end
44
+
45
+ def unbind()
46
+ end
47
+ end
48
+
49
+ class Server
50
+ def self.run()
51
+ @address = Quark::Config.get("quark.address", "127.0.0.1")
52
+ @port = Quark::Config.get("quark.port", 12161)
53
+ @filename = Quark::Config.get("quark.socket")
54
+
55
+ EM.run do
56
+ if not @filename.nil? and File.writable?(File.dirname(@filename))
57
+ puts "Starting server at #{@filename}..."
58
+ EM::start_server(@filename, Quark::SocketServer)
59
+ else
60
+ puts "Starting server on #{@address}:#{@port}..."
61
+ EM::start_server(@address, @port, Quark::SocketServer)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,45 @@
1
+ module Quark
2
+ module SocketServer
3
+ def process_command_fetch(data)
4
+ rv = {}
5
+ keys, from, to = data.split(' ')
6
+ now = (Time.now.to_f * 1000).to_i
7
+
8
+ # negative from/to values are treated as relative number of seconds from now
9
+ if from.to_i < 0
10
+ from = (now - (-1 * from.to_i * 1000))
11
+ end
12
+
13
+ if to.to_i < 0
14
+ to = (now - (-1 * to.to_i * 1000))
15
+ end
16
+
17
+ # if not set, to defaults to now
18
+ to ||= now
19
+
20
+ # find all matching keys from all blocks
21
+ @redis.keys("#{@_prefix}:#{keys}:*").each do |key|
22
+ x, name, block = key.split(':',3)
23
+ block = block.to_i
24
+
25
+ # skip keys whose block falls outside of the given time range (if given)
26
+ next if block > 0 and not from.nil? and block < (from.to_i/1000/3600).to_i
27
+ next if block > 0 and not to.nil? and block > (to.to_i/1000/3600).to_i
28
+
29
+ # get all values, sort by timestamp ascending, and append to ouput (within range, if given)
30
+ @redis.hgetall(key).sort{|a,b|
31
+ a[0] <=> b[0]
32
+ }.each do |timestamp, value|
33
+ timestamp = timestamp.to_i
34
+
35
+ if timestamp >= from.to_i and timestamp <= to.to_i
36
+ rv[name] ||= []
37
+ rv[name] << [timestamp, value.to_f]
38
+ end
39
+ end
40
+ end
41
+
42
+ return rv
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ module Quark
2
+ module SocketServer
3
+ def process_command_observe(data)
4
+ id, value, timestamp = data.chomp.split(' ')
5
+ block = Integer(timestamp.to_i / 1000 / 3600)
6
+
7
+ @redis.hset("#{@_prefix}:#{id}:#{block}", timestamp, value)
8
+ return nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ module Quark
2
+ module SocketServer
3
+ def process_command_peek(data)
4
+ rv = {}
5
+ keys = @redis.keys("#{@_prefix}:#{data}:*")
6
+ peek_keys = []
7
+
8
+ # get unique metric names
9
+ keys.collect{|i|
10
+ i.split(':')[1]
11
+ }.uniq().each do |name|
12
+ # get most recent bucket for this metric
13
+ peek_keys << keys.select{|i|
14
+ i.split(':')[1] == name
15
+ }.sort.last
16
+ end
17
+
18
+ # now that we have the most recent bucket, pull the most recent value from that bucket
19
+ peek_keys.each do |peek|
20
+ rv[peek.split(':')[1]] = @redis.hgetall(peek).sort{|a,b|
21
+ a[0] <=> b[0]
22
+ }.last
23
+ end
24
+
25
+ return rv
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ module Quark
2
+ class Config
3
+ require 'hashlib'
4
+ @_options = Hash.new()
5
+
6
+ def self.get(key, fallback=nil)
7
+ @_options.rget(key, fallback)
8
+ end
9
+
10
+ def self.set(key, value)
11
+ @_options.rset(key, value)
12
+ end
13
+
14
+ def self.all()
15
+ @_options
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quark
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gary Hetzel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multi_json
16
+ requirement: &14255880 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: 1.7.9
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *14255880
25
+ - !ruby/object:Gem::Dependency
26
+ name: eventmachine
27
+ requirement: &14253980 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *14253980
36
+ - !ruby/object:Gem::Dependency
37
+ name: redis
38
+ requirement: &14252900 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 3.0.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *14252900
47
+ - !ruby/object:Gem::Dependency
48
+ name: hiredis
49
+ requirement: &14251500 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.5.2
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *14251500
58
+ - !ruby/object:Gem::Dependency
59
+ name: em-synchrony
60
+ requirement: &14265760 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.0.3
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *14265760
69
+ description: A small service for logging and retrieving timeseries metrics into a
70
+ Redis server
71
+ email: garyhetzel@gmail.com
72
+ executables:
73
+ - quark
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - lib/quark.rb
78
+ - lib/quark/config.rb
79
+ - lib/quark/commands/peek.rb
80
+ - lib/quark/commands/fetch.rb
81
+ - lib/quark/commands/observe.rb
82
+ - bin/quark
83
+ homepage: https://github.com/ghetzel/quark
84
+ licenses: []
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 1.8.11
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: A miniscule timeseries metrics logging service
107
+ test_files: []