influxdb 0.6.2 → 0.8.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.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yml +92 -0
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +34 -0
- data/README.md +80 -34
- data/Rakefile +1 -1
- data/bin/provision.sh +3 -3
- data/influxdb.gemspec +2 -2
- data/lib/influxdb/client.rb +15 -9
- data/lib/influxdb/client/http.rb +11 -1
- data/lib/influxdb/config.rb +7 -5
- data/lib/influxdb/errors.rb +1 -0
- data/lib/influxdb/point_value.rb +4 -1
- data/lib/influxdb/query/batch.rb +9 -3
- data/lib/influxdb/query/cluster.rb +2 -2
- data/lib/influxdb/query/continuous_query.rb +1 -1
- data/lib/influxdb/query/core.rb +11 -7
- data/lib/influxdb/query/database.rb +2 -2
- data/lib/influxdb/query/series.rb +6 -2
- data/lib/influxdb/query/user.rb +8 -8
- data/lib/influxdb/timestamp_conversion.rb +52 -12
- data/lib/influxdb/version.rb +1 -1
- data/lib/influxdb/writer/async.rb +38 -11
- data/lib/influxdb/writer/udp.rb +3 -0
- data/spec/influxdb/cases/async_client_spec.rb +59 -35
- data/spec/influxdb/cases/query_cluster_spec.rb +3 -3
- data/spec/influxdb/cases/query_continuous_query_spec.rb +1 -1
- data/spec/influxdb/cases/query_database_spec.rb +4 -4
- data/spec/influxdb/cases/query_series_spec.rb +24 -8
- data/spec/influxdb/cases/query_user_spec.rb +8 -8
- data/spec/influxdb/cases/querying_issue_7000_spec.rb +1 -1
- data/spec/influxdb/cases/querying_spec.rb +1 -1
- data/spec/influxdb/cases/retry_requests_spec.rb +1 -1
- data/spec/influxdb/cases/udp_client_spec.rb +8 -14
- data/spec/influxdb/client_spec.rb +2 -2
- data/spec/influxdb/config_spec.rb +44 -14
- data/spec/influxdb/logging_spec.rb +3 -0
- data/spec/influxdb/point_value_spec.rb +11 -1
- data/spec/influxdb/time_conversion_spec.rb +19 -0
- data/spec/smoke/smoke_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -4
- metadata +13 -14
- data/.travis.yml +0 -55
data/lib/influxdb/config.rb
CHANGED
@@ -16,6 +16,8 @@ module InfluxDB
|
|
16
16
|
open_timeout: 5,
|
17
17
|
read_timeout: 300,
|
18
18
|
auth_method: nil,
|
19
|
+
proxy_addr: nil,
|
20
|
+
proxy_port: nil,
|
19
21
|
|
20
22
|
# SSL options
|
21
23
|
use_ssl: false,
|
@@ -144,11 +146,11 @@ module InfluxDB
|
|
144
146
|
|
145
147
|
def opts_from_non_params(url)
|
146
148
|
{}.tap do |o|
|
147
|
-
o[:host] = url.host
|
148
|
-
o[:port] = url.port
|
149
|
-
o[:username] = url.user
|
150
|
-
o[:password] = url.password
|
151
|
-
o[:database] = url.path[1..-1]
|
149
|
+
o[:host] = url.host if url.host
|
150
|
+
o[:port] = url.port if url.port
|
151
|
+
o[:username] = URI.decode_www_form_component(url.user) if url.user
|
152
|
+
o[:password] = URI.decode_www_form_component(url.password) if url.password
|
153
|
+
o[:database] = url.path[1..-1] if url.path.length > 1
|
152
154
|
o[:use_ssl] = url.scheme == "https".freeze
|
153
155
|
|
154
156
|
o[:udp] = { host: o[:host], port: o[:port] } if url.scheme == "udp"
|
data/lib/influxdb/errors.rb
CHANGED
@@ -5,6 +5,7 @@ module InfluxDB # :nodoc:
|
|
5
5
|
Error = Class.new StandardError
|
6
6
|
AuthenticationError = Class.new Error
|
7
7
|
ConnectionError = Class.new Error
|
8
|
+
LineProtocolError = Class.new Error
|
8
9
|
SeriesNotFound = Class.new Error
|
9
10
|
JSONParserError = Class.new Error
|
10
11
|
QueryError = Class.new Error
|
data/lib/influxdb/point_value.rb
CHANGED
@@ -13,6 +13,7 @@ module InfluxDB
|
|
13
13
|
def dump
|
14
14
|
dump = @series.dup
|
15
15
|
dump << ",#{@tags}" if @tags
|
16
|
+
dump << " ".freeze if dump[-1] == "\\"
|
16
17
|
dump << " #{@values}"
|
17
18
|
dump << " #{@timestamp}" if @timestamp
|
18
19
|
dump
|
@@ -45,7 +46,9 @@ module InfluxDB
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def escape_values(values)
|
48
|
-
|
49
|
+
if values.nil? || values.empty?
|
50
|
+
raise InfluxDB::LineProtocolError, "Cannot create point with empty values".freeze
|
51
|
+
end
|
49
52
|
|
50
53
|
values.map do |k, v|
|
51
54
|
key = escape(k.to_s, :field_key)
|
data/lib/influxdb/query/batch.rb
CHANGED
@@ -27,7 +27,7 @@ module InfluxDB
|
|
27
27
|
)
|
28
28
|
return [] if statements.empty?
|
29
29
|
|
30
|
-
url = full_url "/query".freeze, query_params(statements.join(";"), opts)
|
30
|
+
url = full_url "/query".freeze, **query_params(statements.join(";"), **opts)
|
31
31
|
series = fetch_series get(url, parse: true, json_streaming: !chunk_size.nil?)
|
32
32
|
|
33
33
|
if denormalize
|
@@ -82,8 +82,14 @@ module InfluxDB
|
|
82
82
|
denormalize_series
|
83
83
|
denormalized_series_list
|
84
84
|
].each do |method_name|
|
85
|
-
|
86
|
-
|
85
|
+
if RUBY_VERSION < "2.7"
|
86
|
+
define_method(method_name) do |*args|
|
87
|
+
client.send method_name, *args
|
88
|
+
end
|
89
|
+
else
|
90
|
+
define_method(method_name) do |*args, **kwargs|
|
91
|
+
client.send method_name, *args, **kwargs
|
92
|
+
end
|
87
93
|
end
|
88
94
|
end
|
89
95
|
end
|
@@ -2,7 +2,7 @@ module InfluxDB
|
|
2
2
|
module Query
|
3
3
|
module Cluster # :nodoc:
|
4
4
|
def create_cluster_admin(username, password)
|
5
|
-
execute("CREATE USER #{username} WITH PASSWORD '#{password}' WITH ALL PRIVILEGES")
|
5
|
+
execute("CREATE USER \"#{username}\" WITH PASSWORD '#{password}' WITH ALL PRIVILEGES")
|
6
6
|
end
|
7
7
|
|
8
8
|
def list_cluster_admins
|
@@ -10,7 +10,7 @@ module InfluxDB
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def revoke_cluster_admin_privileges(username)
|
13
|
-
execute("REVOKE ALL PRIVILEGES FROM #{username}")
|
13
|
+
execute("REVOKE ALL PRIVILEGES FROM \"#{username}\"")
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/influxdb/query/core.rb
CHANGED
@@ -26,7 +26,7 @@ module InfluxDB
|
|
26
26
|
)
|
27
27
|
query = builder.build(query, params)
|
28
28
|
|
29
|
-
url = full_url("/query".freeze, query_params(query, opts))
|
29
|
+
url = full_url("/query".freeze, **query_params(query, **opts))
|
30
30
|
series = fetch_series(get(url, parse: true, json_streaming: !chunk_size.nil?))
|
31
31
|
|
32
32
|
if block_given?
|
@@ -79,7 +79,7 @@ module InfluxDB
|
|
79
79
|
}
|
80
80
|
|
81
81
|
params[:rp] = retention_policy if retention_policy
|
82
|
-
url = full_url("/write", params)
|
82
|
+
url = full_url("/write", **params)
|
83
83
|
post(url, data)
|
84
84
|
end
|
85
85
|
|
@@ -121,15 +121,19 @@ module InfluxDB
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def generate_payload(data)
|
124
|
-
data.map
|
125
|
-
|
126
|
-
|
124
|
+
data.map { |point| generate_point(point) }.join("\n".freeze)
|
125
|
+
end
|
126
|
+
|
127
|
+
def generate_point(point)
|
128
|
+
InfluxDB::PointValue.new(point).dump
|
129
|
+
rescue InfluxDB::LineProtocolError => e
|
130
|
+
(log :error, "Cannot write data: #{e.inspect}") && nil
|
127
131
|
end
|
128
132
|
|
129
133
|
def execute(query, db: nil, **options)
|
130
134
|
params = { q: query }
|
131
135
|
params[:db] = db if db
|
132
|
-
url = full_url("/query".freeze, params)
|
136
|
+
url = full_url("/query".freeze, **params)
|
133
137
|
get(url, options)
|
134
138
|
end
|
135
139
|
|
@@ -143,7 +147,7 @@ module InfluxDB
|
|
143
147
|
series.select { |k, _| %w[columns values].include?(k) }
|
144
148
|
end
|
145
149
|
|
146
|
-
def full_url(path, params
|
150
|
+
def full_url(path, **params)
|
147
151
|
if config.auth_method == "params".freeze
|
148
152
|
params[:u] = config.username
|
149
153
|
params[:p] = config.password
|
@@ -2,11 +2,11 @@ module InfluxDB
|
|
2
2
|
module Query
|
3
3
|
module Database # :nodoc:
|
4
4
|
def create_database(name = nil)
|
5
|
-
execute("CREATE DATABASE #{name || config.database}")
|
5
|
+
execute("CREATE DATABASE \"#{name || config.database}\"")
|
6
6
|
end
|
7
7
|
|
8
8
|
def delete_database(name = nil)
|
9
|
-
execute("DROP DATABASE #{name || config.database}")
|
9
|
+
execute("DROP DATABASE \"#{name || config.database}\"")
|
10
10
|
end
|
11
11
|
|
12
12
|
def list_databases
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module InfluxDB
|
2
2
|
module Query
|
3
3
|
module Series # :nodoc:
|
4
|
-
def delete_series(name)
|
5
|
-
|
4
|
+
def delete_series(name, where: nil, db: config.database)
|
5
|
+
if where
|
6
|
+
execute("DROP SERIES FROM \"#{name}\" WHERE #{where}", db: db)
|
7
|
+
else
|
8
|
+
execute("DROP SERIES FROM \"#{name}\"", db: db)
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
def list_series
|
data/lib/influxdb/query/user.rb
CHANGED
@@ -6,36 +6,36 @@ module InfluxDB
|
|
6
6
|
def create_database_user(database, username, password, options = {})
|
7
7
|
permissions = options.fetch(:permissions, :all)
|
8
8
|
execute(
|
9
|
-
"CREATE user #{username} WITH PASSWORD '#{password}'; "\
|
10
|
-
"GRANT #{permissions.to_s.upcase} ON #{database} TO #{username}"
|
9
|
+
"CREATE user \"#{username}\" WITH PASSWORD '#{password}'; "\
|
10
|
+
"GRANT #{permissions.to_s.upcase} ON \"#{database}\" TO \"#{username}\""
|
11
11
|
)
|
12
12
|
end
|
13
13
|
|
14
14
|
def update_user_password(username, password)
|
15
|
-
execute("SET PASSWORD FOR #{username} = '#{password}'")
|
15
|
+
execute("SET PASSWORD FOR \"#{username}\" = '#{password}'")
|
16
16
|
end
|
17
17
|
|
18
18
|
# permission => [:all]
|
19
19
|
def grant_user_admin_privileges(username)
|
20
|
-
execute("GRANT ALL PRIVILEGES TO #{username}")
|
20
|
+
execute("GRANT ALL PRIVILEGES TO \"#{username}\"")
|
21
21
|
end
|
22
22
|
|
23
23
|
# permission => [:read|:write|:all]
|
24
24
|
def grant_user_privileges(username, database, permission)
|
25
|
-
execute("GRANT #{permission.to_s.upcase} ON #{database} TO #{username}")
|
25
|
+
execute("GRANT #{permission.to_s.upcase} ON \"#{database}\" TO \"#{username}\"")
|
26
26
|
end
|
27
27
|
|
28
28
|
def list_user_grants(username)
|
29
|
-
execute("SHOW GRANTS FOR #{username}")
|
29
|
+
execute("SHOW GRANTS FOR \"#{username}\"")
|
30
30
|
end
|
31
31
|
|
32
32
|
# permission => [:read|:write|:all]
|
33
33
|
def revoke_user_privileges(username, database, permission)
|
34
|
-
execute("REVOKE #{permission.to_s.upcase} ON #{database} FROM #{username}")
|
34
|
+
execute("REVOKE #{permission.to_s.upcase} ON \"#{database}\" FROM \"#{username}\"")
|
35
35
|
end
|
36
36
|
|
37
37
|
def delete_user(username)
|
38
|
-
execute("DROP USER #{username}")
|
38
|
+
execute("DROP USER \"#{username}\"")
|
39
39
|
end
|
40
40
|
|
41
41
|
# => [{"username"=>"usr", "admin"=>true}, {"username"=>"justauser", "admin"=>false}]
|
@@ -1,5 +1,45 @@
|
|
1
1
|
module InfluxDB #:nodoc:
|
2
|
-
|
2
|
+
# Converts a Time to a timestamp with the given precision.
|
3
|
+
#
|
4
|
+
# === Example
|
5
|
+
#
|
6
|
+
# InfluxDB.convert_timestamp(Time.now, "ms")
|
7
|
+
# #=> 1543533308243
|
8
|
+
def self.convert_timestamp(time, precision = "s")
|
9
|
+
factor = TIME_PRECISION_FACTORS.fetch(precision) do
|
10
|
+
raise ArgumentError, "invalid time precision: #{precision}"
|
11
|
+
end
|
12
|
+
|
13
|
+
(time.to_r * factor).to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the current timestamp with the given precision.
|
17
|
+
#
|
18
|
+
# Implementation detail: This does not create an intermediate Time
|
19
|
+
# object with `Time.now`, but directly requests the CLOCK_REALTIME,
|
20
|
+
# which in general is a bit faster.
|
21
|
+
#
|
22
|
+
# This is useful, if you want or need to shave off a few microseconds
|
23
|
+
# from your measurement.
|
24
|
+
#
|
25
|
+
# === Examples
|
26
|
+
#
|
27
|
+
# InfluxDB.now("ns") #=> 1543612126401392625
|
28
|
+
# InfluxDB.now("u") #=> 1543612126401392
|
29
|
+
# InfluxDB.now("ms") #=> 1543612126401
|
30
|
+
# InfluxDB.now("s") #=> 1543612126
|
31
|
+
# InfluxDB.now("m") #=> 25726868
|
32
|
+
# InfluxDB.now("h") #=> 428781
|
33
|
+
def self.now(precision = "s")
|
34
|
+
name, divisor = CLOCK_NAMES.fetch(precision) do
|
35
|
+
raise ArgumentError, "invalid time precision: #{precision}"
|
36
|
+
end
|
37
|
+
|
38
|
+
time = Process.clock_gettime Process::CLOCK_REALTIME, name
|
39
|
+
(time / divisor).to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
TIME_PRECISION_FACTORS = {
|
3
43
|
"ns" => 1e9.to_r,
|
4
44
|
nil => 1e9.to_r,
|
5
45
|
"u" => 1e6.to_r,
|
@@ -8,16 +48,16 @@ module InfluxDB #:nodoc:
|
|
8
48
|
"m" => 1.to_r / 60,
|
9
49
|
"h" => 1.to_r / 60 / 60,
|
10
50
|
}.freeze
|
11
|
-
private_constant :
|
51
|
+
private_constant :TIME_PRECISION_FACTORS
|
12
52
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
53
|
+
CLOCK_NAMES = {
|
54
|
+
"ns" => [:nanosecond, 1],
|
55
|
+
nil => [:nanosecond, 1],
|
56
|
+
"u" => [:microsecond, 1],
|
57
|
+
"ms" => [:millisecond, 1],
|
58
|
+
"s" => [:second, 1],
|
59
|
+
"m" => [:second, 60.to_r],
|
60
|
+
"h" => [:second, (60 * 60).to_r],
|
61
|
+
}.freeze
|
62
|
+
private_constant :CLOCK_NAMES
|
23
63
|
end
|
data/lib/influxdb/version.rb
CHANGED
@@ -9,6 +9,16 @@ module InfluxDB
|
|
9
9
|
def initialize(client, config)
|
10
10
|
@client = client
|
11
11
|
@config = config
|
12
|
+
@stopped = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def stopped?
|
16
|
+
@stopped
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop!
|
20
|
+
worker.stop!
|
21
|
+
@stopped = true
|
12
22
|
end
|
13
23
|
|
14
24
|
def write(data, precision = nil, retention_policy = nil, database = nil)
|
@@ -29,7 +39,7 @@ module InfluxDB
|
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
|
-
class Worker
|
42
|
+
class Worker # rubocop:disable Metrics/ClassLength
|
33
43
|
attr_reader :client,
|
34
44
|
:queue,
|
35
45
|
:threads,
|
@@ -37,7 +47,8 @@ module InfluxDB
|
|
37
47
|
:max_queue_size,
|
38
48
|
:num_worker_threads,
|
39
49
|
:sleep_interval,
|
40
|
-
:block_on_full_queue
|
50
|
+
:block_on_full_queue,
|
51
|
+
:shutdown_timeout
|
41
52
|
|
42
53
|
include InfluxDB::Logging
|
43
54
|
|
@@ -47,7 +58,7 @@ module InfluxDB
|
|
47
58
|
SLEEP_INTERVAL = 5
|
48
59
|
BLOCK_ON_FULL_QUEUE = false
|
49
60
|
|
50
|
-
def initialize(client, config)
|
61
|
+
def initialize(client, config) # rubocop:disable Metrics/MethodLength
|
51
62
|
@client = client
|
52
63
|
config = config.is_a?(Hash) ? config : {}
|
53
64
|
|
@@ -56,10 +67,11 @@ module InfluxDB
|
|
56
67
|
@num_worker_threads = config.fetch(:num_worker_threads, NUM_WORKER_THREADS)
|
57
68
|
@sleep_interval = config.fetch(:sleep_interval, SLEEP_INTERVAL)
|
58
69
|
@block_on_full_queue = config.fetch(:block_on_full_queue, BLOCK_ON_FULL_QUEUE)
|
70
|
+
@shutdown_timeout = config.fetch(:shutdown_timeout, 2 * @sleep_interval)
|
59
71
|
|
60
72
|
queue_class = @block_on_full_queue ? SizedQueue : InfluxDB::MaxQueue
|
61
73
|
@queue = queue_class.new max_queue_size
|
62
|
-
|
74
|
+
@should_stop = false
|
63
75
|
spawn_threads!
|
64
76
|
end
|
65
77
|
|
@@ -68,11 +80,11 @@ module InfluxDB
|
|
68
80
|
end
|
69
81
|
|
70
82
|
def current_threads
|
71
|
-
|
83
|
+
@threads
|
72
84
|
end
|
73
85
|
|
74
86
|
def current_thread_count
|
75
|
-
|
87
|
+
@threads.count
|
76
88
|
end
|
77
89
|
|
78
90
|
# rubocop:disable Metrics/CyclomaticComplexity
|
@@ -87,7 +99,7 @@ module InfluxDB
|
|
87
99
|
@threads << Thread.new do
|
88
100
|
Thread.current[:influxdb] = object_id
|
89
101
|
|
90
|
-
until
|
102
|
+
until @should_stop
|
91
103
|
check_background_queue(thread_num)
|
92
104
|
sleep rand(sleep_interval)
|
93
105
|
end
|
@@ -97,7 +109,7 @@ module InfluxDB
|
|
97
109
|
end
|
98
110
|
end
|
99
111
|
|
100
|
-
def check_background_queue(thread_num =
|
112
|
+
def check_background_queue(thread_num = -1)
|
101
113
|
log(:debug) do
|
102
114
|
"Checking background queue on thread #{thread_num} (#{current_thread_count} active)"
|
103
115
|
end
|
@@ -123,10 +135,10 @@ module InfluxDB
|
|
123
135
|
return if data.values.flatten.empty?
|
124
136
|
|
125
137
|
begin
|
126
|
-
log(:debug) { "Found data in the queue! (#{sizes(data)})" }
|
138
|
+
log(:debug) { "Found data in the queue! (#{sizes(data)}) on thread #{thread_num}" }
|
127
139
|
write(data)
|
128
140
|
rescue StandardError => e
|
129
|
-
log :error, "Cannot write data: #{e.inspect}"
|
141
|
+
log :error, "Cannot write data: #{e.inspect} on thread #{thread_num}"
|
130
142
|
end
|
131
143
|
|
132
144
|
break if queue.length > max_post_points
|
@@ -138,7 +150,22 @@ module InfluxDB
|
|
138
150
|
# rubocop:enable Metrics/AbcSize
|
139
151
|
|
140
152
|
def stop!
|
141
|
-
log(:debug) { "
|
153
|
+
log(:debug) { "Worker is being stopped, flushing queue." }
|
154
|
+
|
155
|
+
# If retry was infinite (-1), set it to zero to give the threads one
|
156
|
+
# last chance to write their data
|
157
|
+
client.config.retry = 0 if client.config.retry < 0
|
158
|
+
|
159
|
+
# Signal the background threads that they should exit.
|
160
|
+
@should_stop = true
|
161
|
+
|
162
|
+
# Wait for the threads to exit and then kill them
|
163
|
+
@threads.each do |t|
|
164
|
+
r = t.join(shutdown_timeout)
|
165
|
+
t.kill if r.nil?
|
166
|
+
end
|
167
|
+
|
168
|
+
# Flush any remaining items in the queue on the main thread
|
142
169
|
check_background_queue until queue.empty?
|
143
170
|
end
|
144
171
|
|
data/lib/influxdb/writer/udp.rb
CHANGED