Skip to content

Commit 74201fe

Browse files
committed
Added support for InfluxDB retention policies
Fixes #7 and #9, Support for multi type points, and long (int64). Also a breaking chnage the DB structure
1 parent 36cc0dd commit 74201fe

21 files changed

+1155
-773
lines changed

AdysTech.InfluxDB.Client.Net.Test/DataGen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.Text;
55
using System.Threading.Tasks;
66

7-
namespace InfluxDB.Client.Test
7+
namespace AdysTech.InfluxDB.Client.Net.Tests
88
{
99
static class DataGen
1010
{
@@ -48,7 +48,7 @@ public static string RandomString()
4848
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ,=/\\";
4949
var stringChars = new StringBuilder (16, 16);
5050

51-
for ( int i = 0; i < stringChars.Length; i++ )
51+
for ( int i = 0; i < stringChars.Capacity; i++ )
5252
{
5353
lock ( syncLock )
5454
{

AdysTech.InfluxDB.Client.Net.Test/InfluxDBClientTest.cs

Lines changed: 269 additions & 301 deletions
Large diffs are not rendered by default.

AdysTech.InfluxDB.Client.Net/AdysTech.InfluxDB.Client.Net.csproj

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,23 @@
4141
<Reference Include="System.Xml" />
4242
</ItemGroup>
4343
<ItemGroup>
44+
<Compile Include="Interfaces\IInfluxRetentionPolicy.cs" />
45+
<Compile Include="Interfaces\IInfluxDatabase.cs" />
46+
<Compile Include="Interfaces\IInfluxMeasurement.cs" />
47+
<Compile Include="DataStructures\InfluxDatabase.cs" />
48+
<Compile Include="DataStructures\InfluxMeasurement.cs" />
4449
<Compile Include="ExtensionMethods.cs" />
45-
<Compile Include="IInfluxDatapoint.cs" />
46-
<Compile Include="IInfluxDBClient.cs" />
47-
<Compile Include="InfluxDatapoint.cs" />
50+
<Compile Include="Interfaces\IInfluxDatapoint.cs" />
51+
<Compile Include="Interfaces\IInfluxDBClient.cs" />
52+
<Compile Include="DataStructures\InfluxDatapoint.cs" />
4853
<Compile Include="InfluxDBClient.cs" />
4954
<Compile Include="DataContracts\InfluxJsonTypes.cs" />
50-
<Compile Include="InfluxDBException.cs" />
55+
<Compile Include="DataStructures\InfluxDBException.cs" />
56+
<Compile Include="DataStructures\InfluxSeries.cs" />
57+
<Compile Include="DataStructures\InfluxRetentionPolicy.cs" />
58+
<Compile Include="DataStructures\InfluxValueField.cs" />
5159
<Compile Include="Properties\AssemblyInfo.cs" />
52-
<Compile Include="ServiceUnavailableException.cs" />
60+
<Compile Include="DataStructures\ServiceUnavailableException.cs" />
5361
</ItemGroup>
5462
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
5563
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

AdysTech.InfluxDB.Client.Net/DataContracts/InfluxJsonTypes.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class Series
1313
[DataMember(Name="name")]
1414
public string SeriesName { get; set; }
1515

16+
[DataMember(Name = "tags")]
17+
public Dictionary<string,string> Tags { get; set; }
18+
1619
[DataMember (Name = "columns")]
1720
public List<string> ColumnHeaders { get; set; }
1821

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace AdysTech.InfluxDB.Client.Net
8+
{
9+
public class InfluxDatabase : IInfluxDatabase
10+
{
11+
public string Name { get; internal set; }
12+
13+
public ICollection<IInfluxMeasurement> Measurements
14+
{
15+
get
16+
{
17+
return _measurements;
18+
}
19+
}
20+
21+
HashSet<IInfluxMeasurement> _measurements;
22+
public InfluxDatabase(string name)
23+
{
24+
Name = name;
25+
_measurements = new HashSet<IInfluxMeasurement>();
26+
}
27+
28+
}
29+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace AdysTech.InfluxDB.Client.Net
8+
{
9+
/// <summary>
10+
/// Represents a single Point (collection of fields) in a series.
11+
/// Each point is uniquely identified by its series and timestamp.
12+
/// </summary>
13+
/// <typeparam name="T">Type of the filed value, Allowed - boolean, string, integer, decimal and double </typeparam>
14+
public class InfluxDatapoint<T> : IInfluxDatapoint, IInfluxDatapoint<T> where T : IComparable, IComparable<T>
15+
{
16+
/// <summary>
17+
/// Gets/Sets the InfluxDB measurement name
18+
/// </summary>
19+
public String MeasurementName { get; set; }
20+
21+
/// <summary>
22+
/// Dictionary storing all Tag/Value combinations
23+
/// </summary>
24+
public Dictionary<string, string> Tags { get; private set; }
25+
26+
/// <summary>
27+
/// The key-value pair in InfluxDB’s data structure that records metadata and the actual data value.
28+
/// Fields are required in InfluxDB’s data structure and they are not indexed.
29+
/// </summary>
30+
public Dictionary<string, T> Fields { get; private set; }
31+
32+
/// <summary>
33+
/// Timestamp for the point, will be converted to Epoch, expcted to be in UTC
34+
/// </summary>
35+
public DateTime UtcTimestamp { get; set; }
36+
37+
/// <summary>
38+
/// Time precision of the point, allowed are Hour->Nano second
39+
/// </summary>
40+
public TimePrecision Precision { get; set; }
41+
42+
/// <summary>
43+
/// Indicates whether a point got saved to InfluxDB successfully
44+
/// </summary>
45+
public bool Saved { get; set; }
46+
47+
public IInfluxRetentionPolicy Retention { get; set; }
48+
49+
public InfluxDatapoint()
50+
{
51+
Type itemType = typeof(T);
52+
if (itemType == typeof(long) ||
53+
itemType == typeof(decimal) ||
54+
itemType == typeof(double) ||
55+
itemType == typeof(int) ||
56+
itemType == typeof(bool) ||
57+
itemType == typeof(string) ||
58+
itemType == typeof(InfluxValueField))
59+
{
60+
Tags = new Dictionary<string, string>();
61+
Fields = new Dictionary<string, T>();
62+
Saved = false;
63+
}
64+
else
65+
{
66+
throw new ArgumentException(itemType.Name + " is not supported by InfluxDB!");
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Initializes tags with a preexisitng dictionary
72+
/// </summary>
73+
/// <param name="tags">Dictionary of tag/value pairs</param>
74+
/// <returns>True:Success, False:Failure</returns>
75+
public bool InitializeTags(IDictionary<string, string> tags)
76+
{
77+
if (Tags.Count > 0)
78+
throw new InvalidOperationException("Tags can be initialized only when the collection is empty");
79+
try
80+
{
81+
Tags = new Dictionary<string, string>(tags);
82+
return true;
83+
}
84+
catch (Exception)
85+
{
86+
Tags = new Dictionary<string, string>();
87+
return false;
88+
}
89+
}
90+
91+
/// <summary>
92+
/// Initializes fields with a preexisting dictionary
93+
/// </summary>
94+
/// <param name="fields">Dictionary of Field/Value pairs</param>
95+
/// <returns>True:Success, False:Failure</returns>
96+
public bool InitializeFields(IDictionary<string, T> fields)
97+
{
98+
if (Fields.Count > 0)
99+
throw new InvalidOperationException("Fields can be initialized only when the collection is empty");
100+
try
101+
{
102+
Fields = new Dictionary<string, T>(fields);
103+
return true;
104+
}
105+
catch (Exception)
106+
{
107+
Fields = new Dictionary<string, T>();
108+
return false;
109+
}
110+
}
111+
112+
/// <summary>
113+
/// Returns the string representing the point in InfluxDB Line protocol
114+
/// </summary>
115+
/// <returns></returns>
116+
/// <see cref="https://docs.influxdata.com/influxdb/v0.12/write_protocols/line/"/>
117+
public string ConvertToInfluxLineProtocol()
118+
{
119+
if (Fields.Count == 0)
120+
throw new InvalidOperationException("InfluxDB needs atleast one field in a line");
121+
if (String.IsNullOrWhiteSpace(MeasurementName))
122+
throw new InvalidOperationException("InfluxDB needs a measurement name to accept a point");
123+
124+
var line = new StringBuilder();
125+
line.AppendFormat("{0}", MeasurementName);
126+
127+
if (Tags.Count > 0)
128+
line.AppendFormat(",{0}", String.Join(",", Tags.Select(t => new StringBuilder().AppendFormat("{0}={1}", t.Key.EscapeChars(), t.Value.EscapeChars()))));
129+
130+
var tType = typeof(T);
131+
string fields;
132+
133+
if (tType == typeof(string))
134+
//string needs escaping, but = is allowed in value
135+
fields = String.Join(",", Fields.Select(v => new StringBuilder().AppendFormat("{0}=\"{1}\"", v.Key.EscapeChars(), v.Value.ToString().EscapeChars(false))));
136+
else if (tType == typeof(long) || tType == typeof(int))
137+
//int needs i suffix
138+
fields = String.Join(",", Fields.Select(v => new StringBuilder().AppendFormat("{0}={1}i", v.Key.EscapeChars(), v.Value)));
139+
else if (tType == typeof(bool))
140+
//bool is okay with True or False
141+
fields = String.Join(",", Fields.Select(v => new StringBuilder().AppendFormat("{0}={1}", v.Key.EscapeChars(), v.Value)));
142+
else if (tType == typeof(double))
143+
////double has to have a . as decimal seperator for Influx
144+
fields = String.Join(",", Fields.Select(v => new StringBuilder().AppendFormat("{0}={1}", v.Key.EscapeChars(), String.Format(System.Globalization.CultureInfo.GetCultureInfo("en-US"), "{0}", v.Value))));
145+
else if (tType == typeof(InfluxValueField))
146+
fields = String.Join(",", Fields.Select(v => new StringBuilder().AppendFormat("{0}={1}", v.Key.EscapeChars(), v.Value.ToString())));
147+
else
148+
throw new ArgumentException(tType + " is not supported by this library at this point");
149+
150+
line.AppendFormat(" {0} {1}", fields, UtcTimestamp != DateTime.MinValue ? UtcTimestamp.ToEpoch(Precision) : DateTime.UtcNow.ToEpoch(Precision));
151+
152+
return line.ToString();
153+
}
154+
155+
}
156+
157+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace AdysTech.InfluxDB.Client.Net
8+
{
9+
public class InfluxMeasurement : IInfluxMeasurement
10+
{
11+
public string Name { get; private set; }
12+
13+
public ICollection<string> Fields
14+
{
15+
get
16+
{
17+
return _fields;
18+
}
19+
}
20+
21+
public ICollection<string> Tags
22+
{
23+
get
24+
{
25+
return _tags;
26+
}
27+
}
28+
29+
HashSet<string> _tags;
30+
HashSet<string> _fields;
31+
32+
public InfluxMeasurement(string name)
33+
{
34+
Name = name;
35+
_fields = new HashSet<string>();
36+
_tags = new HashSet<string>();
37+
}
38+
39+
public override string ToString()
40+
{
41+
return String.Format("{0} : Fileds-{1}, Tags-{2}", Name, Fields.Count, Tags.Count);
42+
}
43+
44+
}
45+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace AdysTech.InfluxDB.Client.Net
8+
{
9+
public class InfluxRetentionPolicy : IInfluxRetentionPolicy
10+
{
11+
/// <summary>
12+
/// Gets/Sets the DB Name that the retention policy will be attached to
13+
/// </summary>
14+
public string DBName { get; set; }
15+
16+
/// <summary>
17+
/// Name of the renteion policy
18+
/// </summary>
19+
public string Name { get; set; }
20+
21+
/// <summary>
22+
/// Duration of the retention period that the policy is defining
23+
/// </summary>
24+
public TimeSpan Duration { get; set; }
25+
26+
/// <summary>
27+
/// If this policy instance is the default policy for the DB
28+
/// </summary>
29+
public bool IsDefault { get; set; }
30+
31+
/// <summary>
32+
/// Number of nodes that the write should be confirmed
33+
/// </summary>
34+
public int ReplicaN { get; set; }
35+
36+
/// <summary>
37+
/// Determines the time range covered by a shard group
38+
/// </summary>
39+
public TimeSpan ShardDuration { get; set; }
40+
41+
/// <summary>
42+
/// if this object is saved to Influx DB
43+
/// </summary>
44+
public bool Saved { get;internal set; }
45+
46+
public InfluxRetentionPolicy()
47+
{
48+
ReplicaN = 1;
49+
}
50+
51+
internal string GetCreateSyntax()
52+
{
53+
if (!String.IsNullOrWhiteSpace(DBName) && !String.IsNullOrWhiteSpace(Name) && Duration >= TimeSpan.FromMinutes(60))
54+
return String.Format("CREATE RETENTION POLICY {0} ON {1} DURATION {2}m REPLICATION {3}{4}", Name, DBName, Duration.TotalMinutes, ReplicaN, IsDefault ? " DEFAULT" : "");
55+
else if (Duration < TimeSpan.FromMinutes(60))
56+
throw new ArgumentException("Minimum retention duration is 1 hour");
57+
else if (String.IsNullOrWhiteSpace(Name))
58+
throw new ArgumentException("Name not set");
59+
else if (String.IsNullOrWhiteSpace(DBName))
60+
throw new ArgumentException("DBName is not set");
61+
return null;
62+
}
63+
}
64+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace AdysTech.InfluxDB.Client.Net
8+
{
9+
public class InfluxSeries
10+
{
11+
public string SeriesName { get; internal set; }
12+
public Dictionary<string, string> Tags { get; internal set; }
13+
public List<dynamic> Entries { get; internal set; }
14+
}
15+
}

0 commit comments

Comments
 (0)