InfluxDB lineprotocol C# write using double-quoted string gives "bad timestamp" error

837 views Asked by At

I'm using the latest C# client and attempting to write data to InfluxDB using the lineprotocol.

This works:

'RobotStarted,instr=GBPUSD,timeframe=12H index=0i 1623046961909067000'

But I receive a parse error when I add in a string field using double-quotes:

{"code":"invalid","message":"unable to parse 'RobotStarted,instr=GBPUSD,timeframe=12H server=\"2021-06-07T06:22:41Z\" index=0i 1623046961909067000': bad timestamp"}

I want to be able to enquote my strings as some can potentially contain spaces etc.

Here's the code I'm using:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core;
using Xunit;

namespace Foo
{
    public class RobotEvent
    {
        public int Index { get; set; }

        public string HostId { get; set; }

        public DateTime OpenTime { get; set; }

        public DateTime ServerTime { get; set; }

        public string SymbolName { get; set; }

        public string TimeFrame { get; set; }
    }

    public class InfluxTests
    {        
        private const string Token = "<token>";
        private const string Url = "<url>";
        private const string Bucket = "<bucket>";
        private const string OrgId = "<org>";

        [Fact]
        public async Task Should_Write_To_InfluxDB()
        {
            using (var repo = new InfluxRepository(Url, Token, Bucket, OrgId))
            {
                try
                {
                    var e = new RobotEvent
                    {
                        HostId = "localhost",
                        OpenTime = DateTime.UtcNow,
                        ServerTime = DateTime.UtcNow,
                        SymbolName = "GBPUSD",
                        TimeFrame = "12H"
                    };

                    repo.WriteEvents(e);

                    var events = await repo.ReadEvents();

                    Assert.NotNull(events);
                    Assert.NotEmpty(events);

                }
                finally
                {
                    await repo.DeleteAll();
                }
            }
        }
    }
    
    public static class InfluxEventExtensions
    {
        private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);

        public static string ToLineProtocol(this RobotEvent e)
        {
            var unixTicks = GetUnixTicks(e.OpenTime);
            var lineProtocol = $"{e.GetType().Name},instr={e.SymbolName},timeframe={e.TimeFrame} server=\"{e.ServerTime.ToISOString()}Z\" index={e.Index}i {unixTicks}00"; // fails
            // var lineProtocol = @$"{e.GetType().Name},instr={e.SymbolName},timeframe={e.TimeFrame} index={e.Index}i {unixTicks}00";  // works
            return lineProtocol;
        }

        private static string ToISOString(this DateTime value)
        {
            return value.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
        }

        private static long GetUnixTicks(DateTime? time = null)
        {
            return ((time ?? DateTime.UtcNow) - UnixEpoch).Ticks;
        }
    }

    public class InfluxRepository : IDisposable
    {
        private readonly string _bucketName;
        private readonly string _orgId;
        private readonly InfluxDBClient _client;

        public InfluxRepository(string url, string token, string bucketName, string orgId)
        {
            _bucketName = bucketName;
            _orgId = orgId;
            _client = InfluxDBClientFactory.Create(InfluxDBClientOptions.Builder
                .CreateNew()
                .AuthenticateToken(token)
                .Bucket(_bucketName)
                .Org(_orgId)
                .LogLevel(LogLevel.Body)
                .Url(url)
                .Build());
        }

        public void WriteEvents(params RobotEvent[] events) 
        {
            using var writeApi = _client.GetWriteApi();
            var records = events.Select(e => e.ToLineProtocol()).ToList();
            writeApi.WriteRecords(_bucketName, _orgId, WritePrecision.Ns, records);
        }

        public async Task<IEnumerable<string>> ReadEvents()
        {     
            var flux =$"from(bucket:\"{_bucketName}\") |> range(start: 0)";
            var fluxTables = await _client.GetQueryApi().QueryAsync(flux, _orgId);
            var results = new List<string>();
            fluxTables.ForEach(fluxTable =>
            {
                fluxTable.Records.ForEach(fluxRecord =>
                {
                    results.Add($"{fluxRecord.GetTime()}: {fluxRecord.GetValue()}");
                });
            });

            return results;
        }

        public async Task DeleteAll()
        {
            var deleteApi = _client.GetDeleteApi();
            var predicate = new DeletePredicateRequest();
            await deleteApi.Delete(predicate, _bucketName, _orgId);
        }

        public void Dispose()
        {
            _client?.Dispose();
        }
    }
}

1

There are 1 answers

0
Jakub Bednář On BEST ANSWER

{"code":"invalid","message":"unable to parse 'RobotStarted,instr=GBPUSD,timeframe=12H server="2021-06-07T06:22:41Z" index=0i 1623046961909067000': bad timestamp"}

you have to separate fields by comma:

RobotStarted,instr=GBPUSD,timeframe=12H server="2021-06-07T06:22:41Z" index=0i 1623046961909067000 => RobotStarted,instr=GBPUSD,timeframe=12H server="2021-06-07T06:22:41Z",index=0i 1623046961909067000