influxdb-ruby

Gem Version 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

Since v0.4.0, this gem requires Ruby >= 2.2.0. Support for MRI < 2.2 is still available in the v0.3.x series, see stable-03 branch for documentation.

Installation

$ [sudo] gem install influxdb

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

Usage

All examples assume you have a require "influxdb" in your code.

Creating a client

Connecting to a single host:

influxdb = InfluxDB::Client.new  # default connects to localhost:8086

# or
influxdb = InfluxDB::Client.new host: "influxdb.domain.com"

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

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

Using a configuration URL

You can also provide a URL to connect to your server. This is particulary useful for 12-factor apps, i.e. you can put the configuration in an environment variable:

url = ENV["INFLUXDB_URL"] || "https://influxdb.example.com:8086/database_name?retry=3"
influxdb = InfluxDB::Client.new url: url

Please note, that the config options found in the URL have a lower precedence than those explicitly given in the options hash. This means, that the following sample will use an open-timeout of 10 seconds:

url = "https://influxdb.example.com:8086/database_name?open_timeout=3"
influxdb = InfluxDB::Client.new url: url, open_timeout: 10

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):

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)

A Note About Time Precision

The default precision (in this Rubygem) is "s" (second), as Ruby's Time#to_i operates on this resolution.

If you write data points with sub-second resolution (for example via Time#to_r), you need to configure your client instance with an appropiate time_precision option and you'll need to provide a timestamp value which reflects this precision:

influxdb = InfluxDB::Client.new time_precision: "ms"
time = (Time.now.to_r * 10**6).to_i
influxdb.write_point("foobar", { values: { n: 42 }, timestamp: time })

Allowed values for time_precision are:

  • "ns" or nil for nanosecond
  • "u" for microsecond
  • "ms" for millisecond
  • "s" for second
  • "m" for minute
  • "h" for hour

Write data with a specific retention policy:

database  = 'site_development'
name      = 'foobar'
precision = 's'
retention = '1h.cpu'

influxdb = InfluxDB::Client.new database,
  username: "foo",
  password: "bar"

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:

database  = 'site_development'
name      = 'foobar'
precision = 's'
retention = '1h.cpu'

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

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):

database = 'site_development'
name     = 'foobar'

influxdb = InfluxDB::Client.new database,
  username: "foo",
  password: "bar",
  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, async: async_options

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

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

name = 'hitchhiker'

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

influxdb.write_point(name, data)

Discard write errors:

influxdb = InfluxDB::Client.new(
  udp: { host: "127.0.0.1", port: 4444 },
  discard_write_errors: true
)

influxdb.write_point('hitchhiker', { values: { value: 666 } })

Querying

database = 'site_development'
influxdb = InfluxDB::Client.new database,
  username: "foo",
  password: "bar"

# 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|
  printf "%s [ %p ]\n", name, tags
  points.each do |pt|
    printf "  -> %p\n", pt
  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

Advanced Topics

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)

Reading data

(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|
  printf "%s [ %p ]\n", name, tags
  points.each do |key, values|
    printf "  %p -> %p\n", key, values
  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] WARN  -- 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] WARN  -- 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] WARN  -- 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] WARN  -- 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] WARN  -- 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] WARN  -- 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] WARN  -- 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] WARN  -- 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.

List of configuration options

This index might be out of date. Please refer to InfluxDB::DEFAULT_CONFIG_OPTIONS, found in lib/influxdb/config.rb for the source of truth.

Category Option Default value Notes
HTTP connection :host or :hosts "localhost" can be an array and can include port
:port 8086 fallback port, unless provided by :host option
:prefix "" URL path prefix (e.g. server is behind reverse proxy)
:username "root" user credentials
:password "root" user credentials
:open_timeout 5 socket timeout
:read_timeout 300 socket timeout
:auth_method "params" "params", "basic_auth" or "none"
Retry :retry -1 max. number of retry attempts (reading and writing)
:initial_delay 0.01 initial wait time (doubles every retry attempt)
:max_delay 30 max. wait time when retrying
SSL/HTTPS :use_ssl false whether or not to use SSL (HTTPS)
:verify_ssl true verify vertificate when using SSL
:ssl_ca_cert false path to or name of CA cert
Database :database empty name of database
:time_precision "s" time resolution for data send to server
:epoch false time resolution for server responses (false = server default)
Writer :async false Async options hash, details here
:udp false UDP connection info, details here
:discard_write_errors false suppress UDP socket errors
Query :chunk_size empty details here
:denormalize true format of result

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.