InfluxDB driver for Elixir
This module is experimental at the moment and may change unexpectedly.
Tested influxdb version: 0.10.0 (see
.travis.yml
to be sure)
Add Instream as a dependency to your mix.exs file:
defp deps do
[ { :instream, "~> 0.10" } ]
endYou should also update your applications to include all necessary projects:
def application do
[ applications: [ :instream ] ]
endTo run the tests you need to have the http-authentication enabled.
Using the statements from the .travis.yml you can generate all necessary
users for the tests with their proper privileges.
Defining a connection requires defining a module:
defmodule MyApp.MyConnection do
use Instream.Connection, otp_app: :my_app
endThe :otp_app name and the name of the module can be freely chosen.
They only need to be linked to an entry in your config.exs:
config :my_app, MyApp.MyConnection,
hosts: [ "localhost" ],
pool: [ max_overflow: 0, size: 1 ],
port: 8086,
scheme: "http",
writer: Instream.Writer.LineNote: While you can define as many hosts as you please only the first one will be used. This is subject to change.
You now have a connection definition you can hook into your supervision tree:
Supervisor.start_link(
[ MyApp.MyConnection.child_spec ],
strategy: :one_for_one
)Only the hosts key is mandatory for a connection configuration. The following
values will be used as defaults if no other value is set:
config :my_app, MyApp.MyConnection,
pool: [ max_overflow: 10, size: 5 ],
port: 8086,
scheme: "http",
writer: Instream.Writer.LineThis also means that per default the connection uses no authentication.
To connect to an influxdb instance with http_auth enabled you have to configure your credentials:
config :my_app, MyApp.MyConnection,
auth: [ method: :basic, username: "root", password: "root" ]For method you can choose between header authentication (basic auth) using
:basic or query parameters using :query. If nothing or an invalid value
is given the connection will be made using :basic authentication.
If you are using the regular line protocol writer Instream.Writer.Line you
are done without having anything to configure. It is used by default and
connects to the port you have configured for connection.
To write points over UDP you can adjust your configuration:
config :my_app, MyApp.MyConnection,
hosts: [ "localhost" ],
port_udp: 8089,
writer: Instream.Writer.UDPThe connection will then write using UDP and connecting to the port :port_udp.
All non-write queries will be send to the regular :port you have configured.
To validate a connection you can send ping requests to the server:
MyApp.MyConnection.ping()The response will be :pong on success or :error on any failure.
To ping "a host other than the first in your configuration" you can pass it explicitly:
MyApp.MyConnection.ping("some.host.name")All values necessary to ping the host (scheme, port, ...) will be taken from the connection used. It does not matter whether the host is configured in that connection or not.
To get InfluxDB to verify status of your cluster you can send a status call:
MyApp.MyConnection.status()As with ping requests you can target a specific host:
MyApp.MyConnection.status("some.host.name")Every query can be executed asynchronously by passing [async: true] to
MyApp.MyConnection.execute(). The result will then always be an immediate
:ok without waiting for the query to be actually executed.
Managing Databases:
# create "my_database"
"my_database"
|> Instream.Cluster.Database.create()
|> MyApp.MyConnection.execute()
# drop "my_database"
"my_database"
|> Instream.Cluster.Database.drop()
|> MyApp.MyConnection.execute()Managing Retention Policies:
# create "my_rp" retention policy
# argument order: policy, database, duration, replication, default
Instream.Cluster.RetentionPolicy.create(
"my_rp", "my_database", "1h", 3, true
)
|> MyApp.MyConnection.execute()
# drop "my_rp" retention policy
Instream.Cluster.RetentionPolicy.drop("my_rp", "my_database")
|> MyApp.MyConnection.execute()Please see the point "Series Definitions" on how to write data to your InfluxDB database.
Reading data:
# passing database to execute/1
"SELECT * FROM some_measurement"
|> MyApp.MyConnection.query(database: "my_database")
# defining database in the query
"SELECT * FROM \"my_database\".\"default\".\"some_measurement\""
|> MyApp.MyConnection.query()
# passing precision (= epoch) for query results
"SELECT * FROM some_measurement"
|> MyApp.MyConnection.query(precision: :minutes)Experimental definition! Will change often and unexpected! (or may disappear...)
Using the query builder you can avoid writing your select statements by hand:
import Instream.Query.Builder
# SELECT one, or, more, fields FROM some_measurement
from(MySeries)
|> select([ "one", "or", "more", "fields" ])
|> MyApp.MyConnection.query()
# SELECT * FROM some_measurement WHERE binary = 'foo' AND numeric = 42
from("some_measurement")
|> where(%{ binary: "foo", numeric: 42 })
|> MyApp.MyConnection.query()If you do not want to define the raw maps for writing data you can pre-define a series for later usage:
defmodule MySeries do
use Instream.Series
series do
database "my_database"
measurement "my_measurement"
tag :bar
tag :foo
field :value
end
endYou can include a default value for tags in your series definition:
series do
tag :host, default: "www"
field :value, default: 100
endThese values will be pre-assigned when using the data struct.
All fields or tags without a default value will be set to nil.
You can then use this module to assemble a data point (one at a time) for writing:
data = %MySeries{}
data = %{ data | fields: %{ data.fields | value: 17 }}
data = %{ data | tags: %{ data.tags | bar: "bar", foo: "foo" }}And then write one or many at once:
data
|> MyApp.MyConnection.write()
# write the point asynchronously
data
|> MyApp.MyConnection.write(async: true)
# write multiple points at once
[ point_1, point_2, point_3 ]
|> MyApp.MyConnection.write()If you want to pass an explicit timestamp to the database you can use the key
:timestamp:
data = %MySeries{}
data = %{ data | timestamp: 1439587926000000000 }The timestamp is (by default) expected to be a nanosecond unix timestamp. To use a different precision (for all points in this write operation!) you can change this value by modifying your write call:
data = %MySeries{}
data = %{ data | timestamp: 1439587926 }
data
|> MyApp.MyConnection.write([ async: true, precision: :seconds ])Supported precision types are:
:hours:minutes:seconds:milli_seconds:micro_seconds:nano_seconds
Please be aware that the UDP protocol writer does not support custom timestamp precisions. All UDP timestamps are implicitly expected to already be at nanosecond precision.