Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SuppressTfmSupportBuildErrors>true</SuppressTfmSupportBuildErrors>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);CS1591;NRS003</NoWarn>
<IsWindows>$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::get_Windows())))</IsWindows>
</PropertyGroup>

Expand Down
32 changes: 32 additions & 0 deletions docs/exp/NRS003.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Redis 8.10 is a relatively new release (at time of writing); the features and API may be subject to change.

This diagnostic covers the library surface for Redis 8.10 time-series features, including:

- *Series-level empty exclusion* (`TS.MRANGE` / `TS.MREVRANGE` `EXCLUDEEMPTY`): omit an entire matching
series from the reply when the queried range and options produce no reported samples for that series.
- *Label metadata queries* (`TS.QUERYLABELS`): return the distinct label names, or the distinct values of a
chosen label, across the series matching a filter.
- *Multi-series pivot ranges* (`TS.NRANGE` / `TS.NREVRANGE`): query a range across several keys and return an
outer-join-by-timestamp pivot, one row per timestamp with one value per key (missing cells reported as NaN).
- *Batched forward reads* (`TS.READ`): read a batch of samples with timestamp at or after a cursor, in
ascending order. The blocking (`BLOCK`) form is intentionally not surfaced, as it does not compose with the
SE.Redis multiplexer.

The corresponding library features must be considered subject to change:

1. Existing bindings may cease working correctly if the underlying server API changes.
2. Changes to the server API may require changes to the library API, manifesting in either/both of build-time
or run-time breaks.

While this seems *unlikely*, it must be considered a possibility. If you acknowledge this, you can suppress
this warning by adding the following to your `csproj` file:

```xml
<NoWarn>$(NoWarn);NRS003</NoWarn>
```

or more granularly / locally in C#:

```c#
#pragma warning disable NRS003
```
1 change: 1 addition & 0 deletions src/NRedisStack/Experiments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace NRedisStack
// NRS002 - Redis 8.8 multi-aggregate time-series
internal static class Experiments
{
public const string Server_8_10 = "NRS003";
// {0} is substituted with the diagnostic id, e.g. NRS042 -> https://redis.github.io/NRedisStack/exp/NRS042
public const string UrlFormat = "https://redis.github.io/NRedisStack/exp/{0}";
}
Expand Down
44 changes: 22 additions & 22 deletions src/NRedisStack/PublicAPI/PublicAPI.Shipped.txt

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions src/NRedisStack/PublicAPI/PublicAPI.Unshipped.txt

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/NRedisStack/ResponseParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,20 @@ public static IReadOnlyList<TimeSeriesTuple> ToTimeSeriesTupleArray(this RedisRe
return list;
}

public static IReadOnlyList<TimeSeriesPivotRow> ToTimeSeriesPivotRowArray(this RedisResult result)
{
RedisResult[] rows = (RedisResult[])result!;
var list = new List<TimeSeriesPivotRow>(rows.Length);
foreach (var row in rows)
{
// each row is [timestamp, [value_0, value_1, ... value_n]]; values use the same NaN-aware
// double parsing as TS.RANGE so missing samples/buckets surface as double.NaN, not null.
RedisResult[] pair = (RedisResult[])row!;
list.Add(new TimeSeriesPivotRow(ToTimeStamp(pair[0]), pair[1].ToDoubleArray()));
}
return list;
}

public static List<TimeSeriesLabel> ToLabelArray(this RedisResult result)
{
if (result.Resp3Type is ResultType.Map) // RESP3; single map
Expand Down
53 changes: 53 additions & 0 deletions src/NRedisStack/TimeSeries/DataTypes/TimeSeriesPivotRow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Text;

namespace NRedisStack.DataTypes;

/// <summary>
/// A single row of a pivot (outer-join-by-timestamp) reply from <c>TS.NRANGE</c> / <c>TS.NREVRANGE</c>:
/// a timestamp plus one cell per input key, in input-key order.
/// </summary>
public readonly struct TimeSeriesPivotRow
{
private readonly IReadOnlyList<double>? _values;

/// <summary>
/// Create a <see cref="TimeSeriesPivotRow"/>.
/// </summary>
/// <param name="timestamp">The row timestamp.</param>
/// <param name="values">One cell per input key, in input-key order.</param>
public TimeSeriesPivotRow(TimeStamp timestamp, IReadOnlyList<double> values)
{
Timestamp = timestamp;
_values = values;
}

/// <summary>
/// The row timestamp.
/// </summary>
public TimeStamp Timestamp { get; }

/// <summary>
/// The row values, one per requested aggregator, laid out in key order then aggregator order within each
/// key. With a single aggregator per key this is one value per key (<c>Values.Count == numkeys</c>); when a
/// key requests multiple aggregators, that key contributes multiple consecutive columns, so
/// <c>Values.Count</c> is the total aggregator count across all keys. A cell is <see cref="double.NaN"/>
/// when the corresponding key has no raw sample at the row timestamp, or no data for the row's aggregation
/// bucket (indistinguishable from a stored NaN) - matching how <c>TS.RANGE</c> surfaces missing values.
/// </summary>
/// <remarks>Never <see langword="null"/>; a <c>default(TimeSeriesPivotRow)</c> reports an empty list.</remarks>
public IReadOnlyList<double> Values => _values ?? Array.Empty<double>();

/// <inheritdoc/>
public override string ToString()
{
var values = Values; // coalesced, never null (guards default(TimeSeriesPivotRow))
var sb = new StringBuilder();
sb.Append("Time: ").Append(Timestamp.ToString()).Append(", Values:");
for (int i = 0; i < values.Count; i++)
{
if (i != 0) sb.Append(',');
sb.Append(values[i]);
}
return sb.ToString();
}
}
Loading
Loading