File: usage.rst

package info (click to toggle)
python-aioinflux 0.9.0-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 320 kB
  • sloc: python: 1,402; makefile: 41
file content (738 lines) | stat: -rw-r--r-- 25,496 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
User Guide
==========

.. contents::
   :local:

TL;DR
-----

This sums most of what you can do with :mod:`aioinflux`:

.. code:: python

    import asyncio
    from aioinflux import InfluxDBClient

    point = {
        'time': '2009-11-10T23:00:00Z',
        'measurement': 'cpu_load_short',
        'tags': {'host': 'server01',
                 'region': 'us-west'},
        'fields': {'value': 0.64}
    }

    async def main():
        async with InfluxDBClient(db='testdb') as client:
           await client.create_database(db='testdb')
           await client.write(point)
           resp = await client.query('SELECT value FROM cpu_load_short')
           print(resp)


    asyncio.get_event_loop().run_until_complete(main())

Client modes
------------

Despite the library's name, |client| can also run in non-async
mode (a.k.a ``blocking``) mode. It can be useful for debugging and exploratory
data analysis.

The running mode for can be switched on-the-fly by changing the ``mode`` attribute:

.. code:: python

    client = InfluxDBClient(mode='blocking')
    client.mode = 'async'

The ``blocking`` mode is implemented through a decorator that automatically runs coroutines on
the event loop as soon as they are generated.
Usage is almost the same as in the ``async`` mode, but without the need of using ``await`` and
being able to run from outside of a coroutine function:

.. code:: python

    client = InfluxDBClient(db='testdb', mode='blocking')
    client.ping()
    client.write(point)
    client.query('SELECT value FROM cpu_load_short')

.. note::

    The need for the ``blocking`` mode has been somewhat supplanted
    by the new async REPL available with the release of IPython 7.0.
    See `this blog post <https://blog.jupyter.org/ipython-7-0-async-repl-a35ce050f7f7>`__ for details.

    If you are having issues running ``blocking`` mode with recent Python/IPython versions,
    see `this issue <https://github.com/gusutabopb/aioinflux/issues/17>`__ for other possible workarounds.

Writing data
------------

To write data to InfluxDB, use |client|'s
|write| method.
Successful writes will return ``True``. In case some error occurs :class:`~aioinflux.client.InfluxDBWriteError`
exception will be raised.

Input data to |write| can be:

1. A mapping (e.g. ``dict``) containing the keys: ``measurement``, ``time``, ``tags``, ``fields``
2. A :class:`pandas.DataFrame` with a |datetimeindex|
3. A user defined class decorated w/ |lineprotocol|
   (**recommended**, see :ref:`below <Writing user-defined class objects>`)
4. A string (``str`` or ``bytes``) properly formatted in InfluxDB's line protocol
5. An iterable of one of the above

Input data in formats 1-3 are serialized into the `line protocol`_ before being written to InfluxDB.
``str`` or ``bytes`` are assumed to already be in line protocol format and are inserted into InfluxDB as they are.
All functionality regarding JSON parsing (InfluxDB's only output format) and serialization to line protocol
(InfluxDB's only input format) is located in the :mod:`~aioinflux.serialization` subpackage.

Beware that serialization is not highly optimized (C extensions / cythonization PRs are welcome!) and may become
a bottleneck depending on your application's performance requirements.
It is, however, reasonably (3-10x) `faster`_ than InfluxDB's `official Python client`_.

.. _`official Python client`: https://github.com/influxdata/influxdb-python
.. _`line protocol`: https://docs.influxdata.com/influxdb/latest/write_protocols/line_protocol_reference/
.. _`faster`: https://gist.github.com/gusutabopb/42550f0f07628ba61b0ed6322f02855b

Writing dictionary-like objects
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. warning::

    This is the same format as the one used by InfluxDB's `official Python client`_ and is implemented
    in Aioinflux for compatibility purposes only.
    Using dictionaries to write data to InfluxDB is slower and more error-prone than the other methods
    provided by Aioinflux and therefore **discouraged**.

Aioinflux accepts any dictionary-like object (mapping) as input.
The dictionary must contain the following keys:

1) **measurement**: Optional. Must be a string-like object. If
   omitted, must be specified when calling |write|
   by passing a ``measurement`` argument.
2) **time**: Optional. The value can be |datetime|,
   date-like string (e.g., ``2017-01-01``, ``2009-11-10T23:00:00Z``) or
   anything else that can be parsed by :class:`pandas.Timestamp`.
   See :ref:`Pandas documentation <pandas:timeseries>` for details.
   If Pandas is not available, |ciso8601|_ is used instead for date-like string parsing.
3) **tags**: Optional. This must contain another mapping of field
   names and values. Both tag keys and values should be strings.
4) **fields**: Mandatory. This must contain another mapping of field
   names and values. Field keys should be strings. Field values can be
   ``float``, ``int``, ``str``, ``bool`` or ``None`` or any its subclasses.
   Attempting to use Numpy types will cause errors as ``np.int64``, ``np.float64``, etc are not
   subclasses of Python's built-in numeric types.
   Use dataframes for writing data using Numpy types.

.. |ciso8601| replace:: ``ciso8601``
.. _ciso8601: https://github.com/closeio/ciso8601/

Any keys other then the above will be ignored when writing data to
InfluxDB.

A typical dictionary-like point would look something like the following:

.. code:: python

    {'time': '2009-11-10T23:00:00Z',
    'measurement': 'cpu_load_short',
    'tags': {'host': 'server01', 'region': 'us-west'},
    'fields': {'value1': 0.64, 'value2': True, 'value3': 10}}

.. note:: **Timestamps and timezones**

   Working with timezones in computing tends to be quite messy.
   To avoid such problems, the `broadly agreed`_ upon idea is to store
   timestamps in UTC. This is how both InfluxDB and Pandas treat timestamps internally.

   Pandas and many other libraries also assume all input timestamps are in UTC unless otherwise
   explicitly noted. Aioinflux does the same and assumes any timezone-unaware |datetime| object
   or datetime-like strings is in UTC.
   Aioinflux does not raise any warnings when timezone-unaware input is passed
   and silently assumes it to be in UTC.

.. _`broadly agreed`: http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/

Writing DataFrames
^^^^^^^^^^^^^^^^^^

Aioinflux also accepts Pandas dataframes as input. The only requirements
for the dataframe is that the index **must** be of type
|datetimeindex|. Also, any column whose ``dtype`` is ``object`` will
be converted to a string representation.

A typical dataframe input should look something like the following:

.. code:: text

                                           LUY       BEM       AJW tag
    2017-06-24 08:45:17.929097+00:00  2.545409  5.173134  5.532397   B
    2017-06-24 10:15:17.929097+00:00 -0.306673 -1.132941 -2.130625   E
    2017-06-24 11:45:17.929097+00:00  0.894738 -0.561979 -1.487940   B
    2017-06-24 13:15:17.929097+00:00 -1.799512 -1.722805 -2.308823   D
    2017-06-24 14:45:17.929097+00:00  0.390137 -0.016709 -0.667895   E

The measurement name must be specified with the ``measurement`` argument
when calling |write|.
Columns that should be treated as tags must be specified by passing a sequence as the ``tag_columns`` argument.
Additional tags (not present in the actual dataframe) can also be passed using arbitrary keyword arguments.

**Example:**

.. code:: python

    client = InfluxDBClient(db='testdb', mode='blocking')
    client.write(df, measurement='prices', tag_columns=['tag'], asset_class='equities')

In the example above, ``df`` is the dataframe we are trying to write to
InfluxDB and ``measurement`` is the measurement we are writing to.

``tag_columns`` is in an optional iterable telling which of the
dataframe columns should be parsed as tag values. If ``tag_columns`` is
not explicitly passed, all columns in the dataframe whose dtype is not
|datetimeindex| will be treated as InfluxDB field values.

Any other keyword arguments passed to |write| are
treated as extra tags which will be attached to the data being written
to InfluxDB. Any string which is a valid `InfluxDB identifier`_ and
valid `Python identifier`_ can be used as an extra tag key (with the
exception of the strings ``data``, ``measurement`` and ``tag_columns``).

See :ref:`API reference <client interface>` for details.

.. _`InfluxDB identifier`: https://docs.influxdata.com/influxdb/latest/query_language/spec/#identifiers
.. _`Python identifier`: https://docs.python.org/3/reference/lexical_analysis.html#identifiers


Writing user-defined class objects
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. versionchanged:: 0.5.0

Aioinflux can add write any arbitrary user-defined class to InfluxDB through the use of the
|lineprotocol| decorator. This decorator monkey-patches an
existing class and adds a ``to_lineprotocol`` method, which is used internally by Aioinflux to serialize
the class data into a InfluxDB-compatible format. In order to generate ``to_lineprotocol``, a typed schema
must be defined using `type hints`_ in the form of type annotations or a schema dictionary.

This is the fastest and least error-prone method of writing data into InfluxDB provided by Aioinflux.

.. _`type hints`: https://docs.python.org/3/library/typing.html

We recommend using |lineprotocol| with :py:class:`~typing.NamedTuple`:


.. code:: python

   from aioinflux import *
   from typing import NamedTuple

   @lineprotocol
   class Trade(NamedTuple):
       timestamp: TIMEINT
       instrument: TAGENUM
       source: TAG
       side: TAG
       price: FLOAT
       size: INT
       trade_id: STR


Alternatively, the functional form of :py:func:`~collections.namedtuple` can also be used:

.. code:: python

    from collections import namedtuple

    schema = dict(
       timestamp=TIMEINT,
       instrument=TAG,
       source=TAG,
       side=TAG,
       price=FLOAT,
       size=INT,
       trade_id=STR,
    )

    # Create class
    Trade = namedtuple('Trade', schema.keys())

    # Monkey-patch existing class and add ``to_lineprotocol``
    Trade = lineprotocol(Trade, schema=schema)


Dataclasses (or any other user-defined class) can be used as well:

.. code:: python

   from dataclasses import dataclass

   @lineprotocol
   @dataclass
   class Trade:
       timestamp: TIMEINT
       instrument: TAGENUM
       source: TAG
       side: TAG
       price: FLOAT
       size: INT
       trade_id: STR

If you want to preserve type annotations for another use,
you can pass your serialization schema as a dictionary as well:

.. code:: python

   @lineprotocol(schema=dict(timestamp=TIMEINT, value=FLOAT))
   @dataclass
   class MyTypedClass:
       timestamp: int
       value: float

    print(MyTypedClass.__annotations__)
    # {'timestamp': <class 'int'>, 'value': <class 'float'>}

    MyTypedClass(1547710904202826000, 2.1).to_lineprotocol()
    # b'MyTypedClass value=2.1 1547710904202826000'


The modified class will have a dynamically generated ``to_lineprotocol`` method which
generates a line protocol representation of the data contained by the object:

.. code:: python

   trade = Trade(
      timestamp=1540184368785116000,
      instrument='AAPL',
      source='NASDAQ',
      side='BUY',
      price=219.23,
      size=100,
      trade_id='34a1e085-3122-429c-9662-7ce82039d287'
   )

   trade.to_lineprotocol()
   # b'Trade,instrument=AAPL,source=NASDAQ,side=BUY price=219.23,size=100i,trade_id="34a1e085-3122-429c-9662-7ce82039d287" 1540184368785116000'

Calling ``to_lineprotocol`` by the end-user is not necessary but may be useful for debugging.

``to_lineprotocol`` is automatically used by |write| when present.

.. code:: python

   client = InfluxDBClient()
   await client.write(trade)  # True


User-defined class schema/type annotations
""""""""""""""""""""""""""""""""""""""""""

In Aioinflux, InfluxDB types (and derived types) are represented by :py:class:`~typing.TypeVar`
defined in :mod:`aioinflux.serialization.usertype` module.
All schema types (type annotations) **must** be one of those types.
The types available are based on the native types of InfluxDB
(see the `InfluxDB docs <https://docs.influxdata.com/influxdb/v1.6/write_protocols/line_protocol_reference/#data-types>`__ for
details), with some extra types to help the serialization to line protocol and/or allow more flexible usage
(such as the use of :py:class:`~enum.Enum` objects).


.. list-table::
   :header-rows: 1
   :widths: 10 30
   :align: center

   * - Type
     - Description
   * - ``MEASUREMENT``
     - Optional. If missing, the measurement becomes the class name
   * - ``TIMEINT``
     - Timestamp is a nanosecond UNIX timestamp
   * - ``TIMESTR``
     - Timestamp is a datetime string (somewhat compliant to ISO 8601)
   * - ``TIMEDT``
     - Timestamp is a |datetime| (or subclasses such as :class:`pandas.Timestamp`)
   * - ``TAG``
     - Treats field as an InfluxDB tag
   * - ``TAGENUM``
     - Same as ``TAG`` but allows the use of :py:class:`~enum.Enum`
   * - ``BOOL``
     - Boolean field
   * - ``INT``
     - Integer field
   * - ``FLOAT``
     - Float field
   * - ``STR``
     - String field
   * - ``ENUM``
     - Same as ``STR`` but allows the use of :py:class:`~enum.Enum`

``TAG*`` types are optional. One and only one ``TIME*`` type must present. At least ONE field type be present.


``@lineprotocol`` options
"""""""""""""""""""""""""

The |lineprotocol| function/decorator provides some options to
customize how object serialization is performed.
See the :ref:`API reference <user-defined classes>` for details.

Performance
"""""""""""

Serialization using |lineprotocol| is about 3x faster
than dictionary-like objects (or about 10x faster than the `official Python client`_).
See this `notebook <https://github.com/gusutabopb/aioinflux/tree/master/notebooks/datapoint_benchmark.ipynb>`__
for a simple benchmark.

Beware that setting ``rm_none=True`` can have substantial performance impact especially when
the number of fields/tags is very large (20+).


Querying data
-------------

Querying data is as simple as passing an InfluxDB query string to |query|:

.. code:: python

    await client.query('SELECT myfield FROM mymeasurement')

By default, this returns JSON data:

.. code:: python

    {'results': [{'series': [{'columns': ['time', 'Price', 'Volume'],
         'name': 'mymeasurement',
         'values': [[1491963424224703000, 5783, 100],
          [1491963424375146000, 5783, 200],
          [1491963428374895000, 5783, 100],
          [1491963429645478000, 5783, 1100],
          [1491963429655289000, 5783, 100],
          [1491963437084443000, 5783, 100],
          [1491963442274656000, 5783, 900],
          [1491963442274657000, 5782, 5500],
          [1491963442274658000, 5781, 3200],
          [1491963442314710000, 5782, 100]]}],
       'statement_id': 0}]}

See `InfluxDB official docs <https://docs.influxdata.com/influxdb/latest/guides/querying_data/#querying-data-using-the-http-api>`_
for more on the InfluxDB's HTTP API specifics.

Output formats
^^^^^^^^^^^^^^

When using, |query| data can return data in one of the following formats:

1) ``json``: Default. Returns a dictionary representation of the JSON response received from InfluxDB.
2) ``dataframe``: Parses the result into a Pandas dataframe(s).
   See :ref:`Retrieving DataFrames` for details.


The output format for can be switched on-the-fly by changing the ``output`` attribute:

.. code:: python

    client = InfluxDBClient(output='dataframe')
    client.mode = 'json'

Beware that when passing ``chunked=True``, the result type will be an async generator.
See :ref:`Chunked responses` for details.


Retrieving DataFrames
^^^^^^^^^^^^^^^^^^^^^

When the client is in ``dataframe`` mode, |query|
will usually return a :class:`pandas.DataFrame`:


.. code:: text

                                      Price  Volume
    2017-04-12 02:17:04.224703+00:00   5783     100
    2017-04-12 02:17:04.375146+00:00   5783     200
    2017-04-12 02:17:08.374895+00:00   5783     100
    2017-04-12 02:17:09.645478+00:00   5783    1100
    2017-04-12 02:17:09.655289+00:00   5783     100
    2017-04-12 02:17:17.084443+00:00   5783     100
    2017-04-12 02:17:22.274656+00:00   5783     900
    2017-04-12 02:17:22.274657+00:00   5782    5500
    2017-04-12 02:17:22.274658+00:00   5781    3200
    2017-04-12 02:17:22.314710+00:00   5782     100

.. note::

   On multi-statement queries and/or statements that return multiple InfluxDB series
   (such as a ``GROUP by "tag"`` query), a list of dictionaries of dataframes will be returned.
   Aioinflux generates a dataframe for each series contained in the JSON returned by InfluxDB.
   See this `Github issue <https://github.com/gusutabopb/aioinflux/issues/19>`__ for further discussion.



When generating dataframes, InfluxDB types are mapped to the following Numpy/Pandas dtypes:

.. list-table::
   :header-rows: 1
   :align: center

   * - InfluxDB type
     - Dataframe column ``dtype``
   * - Float
     - ``float64``
   * - Integer
     - ``int64``
   * - String
     - ``object``
   * - Boolean
     - ``bool``
   * - Timestamp
     - ``datetime64``


Chunked responses
^^^^^^^^^^^^^^^^^
Aioinflux supports InfluxDB chunked queries. Passing ``chunked=True`` when calling
|query|, returns an :py:class:`~collections.abc.AsyncGenerator` object,
which can asynchronously iterated.
Using chunked requests allows response processing to be partially done before
the full response is retrieved, reducing overall query time
(at least in theory - your mileage may vary).

.. code:: python

    chunks = await client.query("SELECT * FROM mymeasurement", chunked=True)
    async for chunk in chunks:
        # do something
        await process_chunk(...)

When using chunked responses with ``dataframe`` output, the following construct may be useful:

.. code:: python

    cursor = await client.query("SELECT * FROM mymeasurement", chunked=True)
    df = pd.concat([i async for i in cursor])

If you need to keep track of when the chunks are being returned,
consider setting up a logging handler at ``DEBUG`` level (see :ref:`Debugging` for details).

See the `InfluxDB official docs <https://docs.influxdata.com/influxdb/v1.7/guides/querying_data/#chunking>`__
for more on chunked responses.

Iterating responses
^^^^^^^^^^^^^^^^^^^

By default, |query| returns a parsed JSON response from InfluxDB.
In order to easily iterate over that JSON response point by point, Aioinflux
provides the |iterpoints| function, which returns a generator object:

.. code:: python

    from aioinflux import iterpoints

    r = client.query('SELECT * from h2o_quality LIMIT 10')
    for i in iterpoints(r):
        print(i)

.. code:: text

    [1439856000000000000, 41, 'coyote_creek', '1']
    [1439856000000000000, 99, 'santa_monica', '2']
    [1439856360000000000, 11, 'coyote_creek', '3']
    [1439856360000000000, 56, 'santa_monica', '2']
    [1439856720000000000, 65, 'santa_monica', '3']

|iterpoints| can also be used with chunked responses:

.. code:: python

    chunks = await client.query('SELECT * from h2o_quality', chunked=True)
    async for chunk in chunks:
        for point in iterpoints(chunk):
            # do something


Using custom parsers
""""""""""""""""""""

By default, the generator returned by |iterpoints|
yields a plain list of values without doing any expensive parsing.
However, in case a specific format is needed, an optional ``parser`` argument can be passed.
``parser`` is a function/callable that takes data point values
and, optionally, a ``meta`` parameter containing which takes a
dictionary containing all or a subset of the following:
``{'columns', 'name', 'tags', 'statement_id'}``.

- Example using a regular function and ``meta``

.. code:: python

    r = await client.query('SELECT * from h2o_quality LIMIT 5')
    for i in iterpoints(r, lambda *x, meta: dict(zip(meta['columns'], x))):
        print(i)

.. code:: text

    {'time': 1439856000000000000, 'index': 41, 'location': 'coyote_creek', 'randtag': '1'}
    {'time': 1439856000000000000, 'index': 99, 'location': 'santa_monica', 'randtag': '2'}
    {'time': 1439856360000000000, 'index': 11, 'location': 'coyote_creek', 'randtag': '3'}
    {'time': 1439856360000000000, 'index': 56, 'location': 'santa_monica', 'randtag': '2'}
    {'time': 1439856720000000000, 'index': 65, 'location': 'santa_monica', 'randtag': '3'}


- Example using a :py:func:`~collections.namedtuple`

.. code:: python

    from collections import namedtuple
    nt = namedtuple('MyPoint', ['time', 'index', 'location', 'randtag'])

    r = await client.query('SELECT * from h2o_quality LIMIT 5')
    for i in iterpoints(r, parser=nt):
        print(i)

.. code:: text

    MyPoint(time=1439856000000000000, index=41, location='coyote_creek', randtag='1')
    MyPoint(time=1439856000000000000, index=99, location='santa_monica', randtag='2')
    MyPoint(time=1439856360000000000, index=11, location='coyote_creek', randtag='3')
    MyPoint(time=1439856360000000000, index=56, location='santa_monica', randtag='2')
    MyPoint(time=1439856720000000000, index=65, location='santa_monica', randtag='3')


Caching query results
^^^^^^^^^^^^^^^^^^^^^

.. versionadded:: v0.7.0

Aioinflux provides an optional caching layer on top of InfluxDB, based on `Redis`_ and :mod:`aioredis`.

The caching functionality is designed for highly iterative/repetitive workloads
(i.e.: machine learning / quantitative finance model tuning)
that constantly query InfluxDB for the same historical data repeatedly.
By saving query results in memory locally, load on your InfluxDB instance can be greatly reduced.

In order to enable/use caching functionality:

1. Install the necessary optional dependencies: ``pip install aioinflux[cache]``

2. Pass Redis host information when initializing :class:`.InfluxDBClient` with the ``redis_opts`` argument.
   ``redis_opts`` takes a dictionary with keyword arguments used when calling :func:`aioredis.create_redis`.

3. When using :meth:`~.InfluxDBClient.query` , set ``use_cache`` to ``True``.
   Even when Redis is properly configured, cache will be ignored unless specified on a per-query basis.

Optionally, to control when the cache expires, use the ``cache_expiry`` argument of :class:`.InfluxDBClient`.
You can also just simply use Redis CLI to clear the cache:

.. code:: bash

    redis-cli -n <db> flushdb

In order to debug whether or not cache is being used or being hit/miss, enable the ``aioinflux`` logger
and set it to ``DEBUG`` level. See :ref:`Debugging` for more details.


.. _Redis: https://redis.io/


Other functionality
-------------------

Authentication
^^^^^^^^^^^^^^

Aioinflux supports basic HTTP authentication provided by :py:class:`aiohttp.BasicAuth`.
Simply pass ``username`` and ``password`` when instantiating |client|:

.. code:: python

    client = InfluxDBClient(username='user', password='pass)


Unix domain sockets
^^^^^^^^^^^^^^^^^^^

If your InfluxDB server uses UNIX domain sockets you can use ``unix_socket``
when instantiating |client|:

.. code:: python

    client = InfluxDBClient(unix_socket='/path/to/socket')

See |unix_connector|_ for details.

.. |unix_connector| replace:: ``aiohttp.UnixConnector``
.. _unix_connector: https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.UnixConnector


Custom timeouts
^^^^^^^^^^^^^^^

.. todo:: TODO

Other ``aiohttp`` functionality
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. todo:: Explain how to customize :class:`aiohttp.ClientSession` creation


HTTPS/SSL
^^^^^^^^^
Aioinflux/InfluxDB uses HTTP by default, but HTTPS can be used by passing ``ssl=True``
when instantiating |client|.
If you are acessing your your InfluxDB instance over the public internet, setting up HTTPS is
`strongly recommended <https://docs.influxdata.com/influxdb/v1.7/administration/https_setup/>`__.


.. code:: python

    client = InfluxDBClient(host='my.host.io', ssl=True)


Database selection
^^^^^^^^^^^^^^^^^^

After the instantiation of the |client| object, database
can be switched by changing the ``db`` attribute:

.. code:: python

    client = InfluxDBClient(db='db1')
    client.db = 'db2'

Beware that differently from some NoSQL databases (such as MongoDB),
InfluxDB requires that a databases is explicitly created (by using the
|CREATE_DATABASE|_ query) before doing any operations on it.

.. |CREATE_DATABASE| replace:: ``CREATE DATABASE``
.. _`CREATE_DATABASE`: https://docs.influxdata.com/influxdb/latest/query_language/database_management/#create-database


Debugging
^^^^^^^^^

If you are having problems while using Aioinflux, enabling logging might be useful.

Below is a simple way to setup logging from your application:

.. code:: python

    import logging

    logging.basicConfig()
    logging.getLogger('aioinflux').setLevel(logging.DEBUG)

For further information about logging, please refer to the
`official documentation <https://docs.python.org/3/library/logging.html>`__.


.. |lineprotocol| replace:: :func:`~aioinflux.serialization.usertype.lineprotocol`
.. |client| replace:: :class:`~aioinflux.client.InfluxDBClient`
.. |write| replace:: :meth:`~aioinflux.client.InfluxDBClient.write`
.. |query| replace:: :meth:`~aioinflux.client.InfluxDBClient.query`
.. |iterpoints| replace:: :func:`~aioinflux.iterutils.iterpoints`
.. |datetimeindex| replace:: :class:`~pandas.DatetimeIndex`
.. |datetime| replace:: :py:class:`datetime.datetime`