feat(batch13): port filestore filtered-pending query helpers
This commit is contained in:
@@ -2132,6 +2132,174 @@ public sealed class JetStreamFileStore : IStreamStore, IDisposable
|
||||
return (bi + 1, null);
|
||||
}
|
||||
|
||||
// Lock should be held by caller.
|
||||
private void NumFilteredPending(string filter, SimpleState ss)
|
||||
=> NumFilteredPendingWithLast(filter, includeLast: true, ss);
|
||||
|
||||
// Lock should be held by caller.
|
||||
private void NumFilteredPendingNoLast(string filter, SimpleState ss)
|
||||
=> NumFilteredPendingWithLast(filter, includeLast: false, ss);
|
||||
|
||||
// Lock should be held by caller.
|
||||
private void NumFilteredPendingWithLast(string filter, bool includeLast, SimpleState ss)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(ss);
|
||||
|
||||
var isAll = string.IsNullOrEmpty(filter) || filter == ">";
|
||||
if (isAll)
|
||||
{
|
||||
ss.First = _state.FirstSeq;
|
||||
ss.Last = _state.LastSeq;
|
||||
ss.Msgs = _state.Msgs;
|
||||
return;
|
||||
}
|
||||
|
||||
ss.First = 0;
|
||||
ss.Last = 0;
|
||||
ss.Msgs = 0;
|
||||
|
||||
if (_psim == null)
|
||||
return;
|
||||
|
||||
var wc = SubscriptionIndex.SubjectHasWildcard(filter);
|
||||
var filterBytes = Encoding.UTF8.GetBytes(filter);
|
||||
var start = uint.MaxValue;
|
||||
uint stop = 0;
|
||||
|
||||
if (wc)
|
||||
{
|
||||
_psim.Match(filterBytes, (_, psi) =>
|
||||
{
|
||||
ss.Msgs += psi.Total;
|
||||
if (psi.Fblk < start)
|
||||
start = psi.Fblk;
|
||||
if (psi.Lblk > stop)
|
||||
stop = psi.Lblk;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var (psi, ok) = _psim.Find(filterBytes);
|
||||
if (ok && psi != null)
|
||||
{
|
||||
ss.Msgs += psi.Total;
|
||||
start = psi.Fblk;
|
||||
stop = psi.Lblk;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop == 0 || start == uint.MaxValue)
|
||||
return;
|
||||
|
||||
if (_bim.TryGetValue(start, out var mb) && mb != null)
|
||||
{
|
||||
var (_, first, _) = FilteredPendingInBlock(mb, filter, wc);
|
||||
ss.First = first;
|
||||
}
|
||||
|
||||
if (ss.First == 0)
|
||||
{
|
||||
uint? correctedFirstBlock = null;
|
||||
for (ulong i = (ulong)start + 1; i <= stop; i++)
|
||||
{
|
||||
if (!_bim.TryGetValue((uint)i, out var candidate) || candidate == null)
|
||||
continue;
|
||||
|
||||
var (_, first, _) = FilteredPendingInBlock(candidate, filter, wc);
|
||||
if (first > 0)
|
||||
{
|
||||
ss.First = first;
|
||||
correctedFirstBlock = (uint)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (correctedFirstBlock is { } corrected)
|
||||
SchedulePsiFirstBlockCorrection(filter, wc, corrected);
|
||||
}
|
||||
|
||||
if (includeLast && _bim.TryGetValue(stop, out var lastMb) && lastMb != null)
|
||||
{
|
||||
var (_, _, last) = FilteredPendingInBlock(lastMb, filter, wc);
|
||||
ss.Last = last;
|
||||
}
|
||||
}
|
||||
|
||||
// Caller should hold a read lock on fs + message block lock if concurrent mutations are possible.
|
||||
private static (ulong Total, ulong First, ulong Last) FilteredPendingInBlock(MessageBlock mb, string filter, bool wc)
|
||||
{
|
||||
if (mb.Fss == null)
|
||||
return (0, 0, 0);
|
||||
|
||||
var filterBytes = Encoding.UTF8.GetBytes(filter);
|
||||
ulong total = 0;
|
||||
ulong first = 0;
|
||||
ulong last = 0;
|
||||
|
||||
if (wc)
|
||||
{
|
||||
mb.Fss.Match(filterBytes, (_, ss) =>
|
||||
{
|
||||
total += ss.Msgs;
|
||||
if (first == 0 || (ss.First > 0 && ss.First < first))
|
||||
first = ss.First;
|
||||
if (ss.Last > last)
|
||||
last = ss.Last;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var (ss, ok) = mb.Fss.Find(filterBytes);
|
||||
if (ok && ss != null)
|
||||
{
|
||||
total = ss.Msgs;
|
||||
first = ss.First;
|
||||
last = ss.Last;
|
||||
}
|
||||
}
|
||||
|
||||
return (total, first, last);
|
||||
}
|
||||
|
||||
private void SchedulePsiFirstBlockCorrection(string filter, bool wc, uint firstBlock)
|
||||
{
|
||||
if (_psim == null)
|
||||
return;
|
||||
|
||||
var filterBytes = Encoding.UTF8.GetBytes(filter);
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
_mu.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (_psim == null)
|
||||
return;
|
||||
|
||||
if (!wc)
|
||||
{
|
||||
var (info, ok) = _psim.Find(filterBytes);
|
||||
if (ok && info != null && firstBlock > info.Fblk)
|
||||
info.Fblk = firstBlock;
|
||||
return;
|
||||
}
|
||||
|
||||
_psim.Match(filterBytes, (_, psi) =>
|
||||
{
|
||||
if (firstBlock > psi.Fblk)
|
||||
psi.Fblk = firstBlock;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
_mu.ExitWriteLock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IStreamStore — type / state
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user