influxdb-ruby

Build Status

The official Ruby client library for InfluxDB. Maintained by @toddboom and @dmke.

Contents

Platform support

Support for InfluxDB v0.8.x is now deprecated. The final version of this library that will support the older InfluxDB interface is v0.1.9, which is available as a gem and tagged on this repository.

If you're reading this message, then you should only expect support for InfluxDB v0.9.1 and higher.

Ruby support

This gem should work with Ruby 1.9+, but starting with v0.4, we'll likely drop Ruby 1.9 support.

Please note that for Ruby 1.9, you'll need to install the JSON gem in version 1.8.x yourself, for example by pinning the version in your Gemfile (i.e. gem "json", "~> 1.8.3").

Installation

$ [sudo] gem install influxdb

Or add it to your Gemfile, and run bundle install.

Usage

Creating a client

Connecting to a single host:

require 'influxdb'

influxdb = InfluxDB::Client.new host: "influxdb.domain.com"
# or
influxdb = InfluxDB::Client.new  # no host given defaults connecting to localhost

Connecting to multiple hosts (with built-in load balancing and failover):

require 'influxdb'

influxdb = InfluxDB::Client.new hosts: ["influxdb1.domain.com", "influxdb2.domain.com"]

Administrative tasks

Create a database:

database = 'site_development'

influxdb.create_database(database)

Delete a database:

database = 'site_development'

influxdb.delete_database(database)

List databases:

influxdb.list_databases

Create a user for a database:

database = 'site_development'
new_username = 'foo'
new_password = 'bar'
permission = :write

# with all permissions
influxdb.create_database_user(database, new_username, new_password)

# with specified permission - options are: :read, :write, :all
influxdb.create_database_user(database, new_username, new_password, permissions: permission)

Update a user password:

username = 'foo'
new_password = 'bar'

influxdb.update_user_password(username, new_password)

Grant user privileges on database:

username = 'foobar'
database = 'foo'
permission = :read # options are :read, :write, :all

influxdb.grant_user_privileges(username, database, permission)

Revoke user privileges from database:

username = 'foobar'
database = 'foo'
permission = :write # options are :read, :write, :all

influxdb.revoke_user_privileges(username, database, permission)

Delete a user:

username = 'foobar'

influxdb.delete_user(username)

List users:

influxdb.list_users

Create cluster admin:

username = 'foobar'
password = 'pwd'

influxdb.create_cluster_admin(username, password)

List cluster admins:

influxdb.list_cluster_admins

Revoke cluster admin privileges from user:

username = 'foobar'

influxdb.revoke_cluster_admin_privileges(username)

Continuous Queries

List continuous queries of a database:

database = 'foo'

influxdb.list_continuous_queries(database)

Create a continuous query for a database:

database = 'foo'
name = 'clicks_count'
query = 'SELECT COUNT(name) INTO clicksCount_1h FROM clicks GROUP BY time(1h)'

influxdb.create_continuous_query(name, database, query)

Additionally, you can specify the resample interval and the time range over which the CQ runs:

influxdb.create_continuous_query(name, database, query, resample_every: "10m", resample_for: "65m")

Delete a continuous query from a database:

database = 'foo'
name = 'clicks_count'

influxdb.delete_continuous_query(name, database)

Retention Policies

List retention policies of a database:

database = 'foo'

influxdb.list_retention_policies(database)

Create a retention policy for a database:

database    = 'foo'
name        = '1h.cpu'
duration    = '10m'
replication = 2

influxdb.create_retention_policy(name, database, duration, replication)

Delete a retention policy from a database:

database = 'foo'
name     = '1h.cpu'

influxdb.delete_retention_policy(name, database)

Alter a retention policy for a database:

database    = 'foo'
name        = '1h.cpu'
duration    = '10m'
replication = 2

influxdb.alter_retention_policy(name, database, duration, replication)

Writing data

Write some data:

username = 'foo'
password = 'bar'
database = 'site_development'
name     = 'foobar'

influxdb = InfluxDB::Client.new database, username: username, password: password

# Enumerator that emits a sine wave
Value = (0..360).to_a.map {|i| Math.send(:sin, i / 10.0) * 10 }.each

loop do
  data = {
    values: { value: Value.next },
    tags:   { wave: 'sine' } # tags are optional
  }

  influxdb.write_point(name, data)

  sleep 1
end

Write data with time precision (precision can be set in 2 ways):

require 'influxdb'

username       = 'foo'
password       = 'bar'
database       = 'site_development'
name           = 'foobar'
time_precision = 's'

# either in the client initialization:
influxdb = InfluxDB::Client.new database,
  username: username,
  password: password,
  time_precision: time_precision

data = {
  values: { value: 0 },
  timestamp: Time.now.to_i # timestamp is optional, if not provided point will be saved with current time
}

influxdb.write_point(name, data)

# or in a method call:
influxdb.write_point(name, data, time_precision)

Write data with a specific retention policy:

require 'influxdb'

username  = 'foo'
password  = 'bar'
database  = 'site_development'
name      = 'foobar'
precision = 's'
retention = '1h.cpu'

influxdb = InfluxDB::Client.new database,
  username: username,
  password: password

data = {
  values:    { value: 0 },
  tags:      { foo: 'bar', bar: 'baz' }
  timestamp: Time.now.to_i
}

influxdb.write_point(name, data, precision, retention)

Write data while choosing the database:

require 'influxdb'

username  = 'foo'
password  = 'bar'
database  = 'site_development'
name      = 'foobar'
precision = 's'
retention = '1h.cpu'

influxdb = InfluxDB::Client.new {
  username: username,
  password: password
}

data = {
  values:    { value: 0 },
  tags:      { foo: 'bar', bar: 'baz' }
  timestamp: Time.now.to_i
}

influxdb.write_point(name, data, precision, retention, database)

Write multiple points in a batch (performance boost):


data = [
  {
    series: 'cpu',
    tags:   { host: 'server_1', region: 'us' },
    values: { internal: 5, external: 0.453345 }
  },
  {
    series: 'gpu',
    values: { value: 0.9999 },
  }
]

influxdb.write_points(data)

# you can also specify precision in method call

precision = 'm'
influxdb.write_points(data, precision)

Write multiple points in a batch with a specific retention policy:

data = [
  {
    series: 'cpu',
    tags:   { host: 'server_1', region: 'us' },
    values: { internal: 5, external: 0.453345 }
  },
  {
    series: 'gpu',
    values: { value: 0.9999 },
  }
]

precision = 'm'
retention = '1h.cpu'
influxdb.write_points(data, precision, retention)

Write asynchronously (note that a retention policy cannot be specified for asynchronous writes):

require 'influxdb'

username = 'foo'
password = 'bar'
database = 'site_development'
name     = 'foobar'

influxdb = InfluxDB::Client.new database,
  username: username,
  password: password,
  async:    true

data = {
  values:    { value: 0 },
  tags:      { foo: 'bar', bar: 'baz' },
  timestamp: Time.now.to_i
}

influxdb.write_point(name, data)

Using async: true is a shortcut for the following:

async_options = {
  # number of points to write to the server at once
  max_post_points:    1000,
  # queue capacity
  max_queue_size:     10_000,
  # number of threads
  num_worker_threads: 3,
  # max. time (in seconds) a thread sleeps before
  # checking if there are new jobs in the queue
  sleep_interval:     5
}

influxdb = InfluxDB::Client.new database,
  username: username,
  password: password,
  async:    async_options

Write data via UDP (note that a retention policy cannot be specified for UDP writes):

require 'influxdb'
host = '127.0.0.1'
port = 4444

influxdb = InfluxDB::Client.new udp: { host: host, port: port }

name = 'hitchhiker'

data = {
  values: { value: 666 },
  tags:   { foo: 'bar', bar: 'baz' }
}

influxdb.write_point(name, data)

Reading data

Querying

username = 'foo'
password = 'bar'
database = 'site_development'

influxdb = InfluxDB::Client.new database,
  username: username,
  password: password

# without a block:
influxdb.query 'select * from time_series_1 group by region'

# results are grouped by name, but also their tags:
#
# [
#   {
#     "name"=>"time_series_1",
#     "tags"=>{"region"=>"uk"},
#     "values"=>[
#       {"time"=>"2015-07-09T09:03:31Z", "count"=>32, "value"=>0.9673},
#       {"time"=>"2015-07-09T09:03:49Z", "count"=>122, "value"=>0.4444}
#     ]
#   },
#   {
#     "name"=>"time_series_1",
#     "tags"=>{"region"=>"us"},
#     "values"=>[
#       {"time"=>"2015-07-09T09:02:54Z", "count"=>55, "value"=>0.4343}
#     ]
#   }
# ]

# with a block:
influxdb.query 'select * from time_series_1 group by region' do |name, tags, points|
  puts "#{name} [ #{tags.inspect} ]"
  points.each do |pt|
    puts "  -> #{pt.inspect}"
  end
end

# result:
# time_series_1 [ {"region"=>"uk"} ]
#   -> {"time"=>"2015-07-09T09:03:31Z", "count"=>32, "value"=>0.9673}
#   -> {"time"=>"2015-07-09T09:03:49Z", "count"=>122, "value"=>0.4444}]
# time_series_1 [ {"region"=>"us"} ]
#   -> {"time"=>"2015-07-09T09:02:54Z", "count"=>55, "value"=>0.4343}

If you would rather receive points with integer timestamp, it's possible to set epoch parameter:

# globally, on client initialization:
influxdb = InfluxDB::Client.new database, epoch: 's'

influxdb.query 'select * from time_series group by region'
# [
#   {
#     "name"=>"time_series",
#     "tags"=>{"region"=>"uk"},
#     "values"=>[
#       {"time"=>1438411376, "count"=>32, "value"=>0.9673}
#     ]
#   }
# ]

# or for a specific query call:
influxdb.query 'select * from time_series group by region', epoch: 'ms'
# [
#   {
#     "name"=>"time_series",
#     "tags"=>{"region"=>"uk"},
#     "values"=>[
#       {"time"=>1438411376000, "count"=>32, "value"=>0.9673}
#     ]
#   }
# ]

Working with parameterized query strings works as expected:

influxdb = InfluxDB::Client.new database

named_parameter_query = "select * from time_series_0 where time > %{min_time}"
influxdb.query named_parameter_query, params: { min_time: 0 }
# compiles to:
#   select * from time_series_0 where time > 0

positional_params_query = "select * from time_series_0 where f = %{1} and i < %{2}"
influxdb.query positional_params_query, params: ["foobar", 42]
# compiles to (note the automatic escaping):
#   select * from time_series_0 where f = 'foobar' and i < 42

(De-) Normalization

By default, InfluxDB::Client will denormalize points (received from InfluxDB as columns and rows). If you want to get raw data add denormalize: false to the initialization options or to query itself:

influxdb.query 'select * from time_series_1 group by region', denormalize: false

# [
#   {
#     "name"=>"time_series_1",
#     "tags"=>{"region"=>"uk"},
#     "columns"=>["time", "count", "value"],
#     "values"=>[
#       ["2015-07-09T09:03:31Z", 32, 0.9673],
#       ["2015-07-09T09:03:49Z", 122, 0.4444]
#     ]
#   },
#   {
#     "name"=>"time_series_1",
#     "tags"=>{"region"=>"us"},
#     "columns"=>["time", "count", "value"],
#     "values"=>[
#       ["2015-07-09T09:02:54Z", 55, 0.4343]
#     ]
#   }
# ]


influxdb.query 'select * from time_series_1 group by region', denormalize: false do |name, tags, points|
  puts "#{name} [ #{tags.inspect} ]"
  points.each do |key, values|
    puts "  #{key.inspect} -> #{values.inspect}"
  end
end


# time_series_1 [ {"region"=>"uk"} ]
#   columns -> ["time", "count", "value"]
#   values -> [["2015-07-09T09:03:31Z", 32, 0.9673], ["2015-07-09T09:03:49Z", 122, 0.4444]]}
# time_series_1 [ {"region"=>"us"} ]
#   columns -> ["time", "count", "value"]
#   values -> [["2015-07-09T09:02:54Z", 55, 0.4343]]}

You can also pick the database to query from:

influxdb.query 'select * from time_series_1', database: 'database'

Streaming response

If you expect large quantities of data in a response, you may want to enable JSON streaming by setting a chunk_size:

influxdb = InfluxDB::Client.new database,
  username:   username,
  password:   password,
  chunk_size: 10000

See the official documentation for more details.

Retry

By default, InfluxDB::Client will keep trying (with exponential fall-off) to connect to the database until it gets a connection. If you want to retry only a finite number of times (or disable retries altogether), you can pass the :retry option.

:retry can be either true, false or an Integer to retry infinite times, disable retries or retry a finite number of times, respectively. Passing 0 is equivalent to false and -1 is equivalent to true.

$ irb -r influxdb
> influxdb = InfluxDB::Client.new 'database', retry: 8
=> #<InfluxDB::Client:0x00000002bb5ce0 ...>

> influxdb.query 'select * from serie limit 1'
E, [2016-08-31T23:55:18.287947 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.01s.
E, [2016-08-31T23:55:18.298455 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.02s.
E, [2016-08-31T23:55:18.319122 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.04s.
E, [2016-08-31T23:55:18.359785 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.08s.
E, [2016-08-31T23:55:18.440422 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.16s.
E, [2016-08-31T23:55:18.600936 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.32s.
E, [2016-08-31T23:55:18.921740 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 0.64s.
E, [2016-08-31T23:55:19.562428 #23476] ERROR -- InfluxDB: Failed to contact host localhost: #<Errno::ECONNREFUSED: Failed to open TCP connection to localhost:8086 (Connection refused - connect(2) for "localhost" port 8086)> - retrying in 1.28s.
InfluxDB::ConnectionError: Tried 8 times to reconnect but failed.

Testing

git clone git@github.com:influxdata/influxdb-ruby.git
cd influxdb-ruby
bundle
bundle exec rake

Contributing

  • Fork this repository on GitHub.
  • Make your changes.
    • Add tests.
    • Add an entry in the CHANGELOG.md in the "unreleased" section on top.
  • Run the tests: bundle exec rake.
  • Send a pull request.
    • Please rebase against the master branch.
  • If your changes look good, we'll merge them.