public AllocatedMemoryData Clone(JsonOperationContext context, out TimeSeriesValuesSegment segment) { // for better reuse let use the const size of 'MaxSegmentSize' var memory = context.GetMemory(TimeSeriesStorage.MaxSegmentSize); Memory.Set(memory.Address, 0, TimeSeriesStorage.MaxSegmentSize); CopyTo(memory.Address); segment = new TimeSeriesValuesSegment(memory.Address, _capacity); return(memory); }
private static DateTime GetLastLiveTimestamp(DocumentsOperationContext context, TimeSeriesValuesSegment segment, DateTime baseline) { return(segment.NumberOfEntries == segment.NumberOfLiveEntries ? segment.GetLastTimestamp(baseline) // all values are alive, so we can get the last value fast : segment.YieldAllValues(context, baseline, includeDead: false).Last().Timestamp); }
public long UpdateStats(DocumentsOperationContext context, TimeSeriesSliceHolder slicer, CollectionName collection, TimeSeriesValuesSegment segment, DateTime baseline, int modifiedEntries) { long previousCount; DateTime start, end; context.DocumentDatabase.Metrics.TimeSeries.PutsPerSec.MarkSingleThreaded(modifiedEntries); context.DocumentDatabase.Metrics.TimeSeries.BytesPutsPerSec.MarkSingleThreaded(segment.NumberOfBytes); var table = GetOrCreateTable(context.Transaction.InnerTransaction, collection); using (ReadStats(context, table, slicer, out previousCount, out start, out end, out var name)) { var liveEntries = segment.NumberOfLiveEntries; if (liveEntries > 0) { HandleLiveSegment(); } if (liveEntries == 0) { if (TryHandleDeadSegment() == false) { // this ts was completely deleted start = end = default; } } var count = previousCount + liveEntries; using (table.Allocate(out var tvb)) { tvb.Add(slicer.StatsKey); tvb.Add(GetPolicy(slicer)); tvb.Add(Bits.SwapBytes(start.Ticks)); tvb.Add(end); tvb.Add(count); tvb.Add(name); table.Set(tvb); } return(count); } void HandleLiveSegment() { if (segment.NumberOfEntries == 1 && start > end) { // new series start = end = baseline; return; } var lastTimestamp = GetLastLiveTimestamp(context, segment, baseline); if (lastTimestamp > end) { end = lastTimestamp; // found later end } else { var reader = _timeSeriesStorage.GetReader(context, slicer.DocId, slicer.Name, start, DateTime.MaxValue); var last = reader.Last(); var lastValueInCurrentSegment = reader.ReadBaselineAsDateTime() == baseline; end = lastValueInCurrentSegment ? lastTimestamp : last.Timestamp; } var first = segment.YieldAllValues(context, baseline, includeDead: false).First().Timestamp; if (first < start) { start = first; // found earlier start } if (baseline <= start && first >= start) { // start was removed start = first; } } bool TryHandleDeadSegment() { if (previousCount == 0) { return(false); // if current and previous are zero it means that this time-series was completely deleted } var readerOfFirstValue = _timeSeriesStorage.GetReader(context, slicer.DocId, slicer.Name, DateTime.MinValue, DateTime.MaxValue); readerOfFirstValue.First(); var firstValueInCurrentSegment = readerOfFirstValue.ReadBaselineAsDateTime() == baseline; var last = segment.GetLastTimestamp(baseline); if (baseline <= start && last >= start || firstValueInCurrentSegment) { // start was removed, need to find the next start // this segment isn't relevant, so let's get the next one var next = _timeSeriesStorage.GetReader(context, slicer.DocId, slicer.Name, start, DateTime.MaxValue).NextSegmentBaseline(); var reader = _timeSeriesStorage.GetReader(context, slicer.DocId, slicer.Name, next, DateTime.MaxValue); var first = reader.First(); if (first == default) { return(false); } start = first.Timestamp; } var readerOfLastValue = _timeSeriesStorage.GetReader(context, slicer.DocId, slicer.Name, start, DateTime.MaxValue); readerOfLastValue.Last(); var lastValueInCurrentSegment = readerOfLastValue.ReadBaselineAsDateTime() == baseline; if (baseline <= end && end <= last || lastValueInCurrentSegment) { var lastEntry = _timeSeriesStorage.GetReader(context, slicer.DocId, slicer.Name, start, baseline.AddMilliseconds(-1)).Last(); if (lastEntry == default) { return(false); } end = lastEntry.Timestamp; } return(true); } }