Engine API reference
AnalyticsEngine is the application-lifetime owner of a dataset. It implements IAnalyticsTracker, synchronous disposal, and asynchronous disposal.
Creation and ownership
| Member | Purpose |
|---|---|
| AnalyticsEngine(AnalyticsOptions) | Open or create a dataset with explicit configuration |
| Open(directory, configure) | Convenience factory with a configuration callback |
| TimeZone and DefaultSite | Read the active reporting defaults |
| Web | Typed website reporting facade |
| Storage | Advanced access to engine-owned stores |
Options are copied at construction. One engine exclusively owns the directory until it closes.
Keep the engine open while the application runs. Ordinary queries do not require an explicit flush. Dispose the engine at shutdown to save remaining work, or let ASP.NET Core dependency injection manage its lifetime.
Tracking
| Member | Return | Queue behavior |
|---|---|---|
| Track(event) | bool | Accept immediately if space exists; otherwise Drop or synchronous Wait |
| TrackAsync(event, cancellationToken) | ValueTask of bool | Always wait asynchronously for queue space |
| IAnalyticsTracker | Tracking interface | Useful for application code and test fakes |
False indicates that input was not queued, such as policy filtering, rejection, disposal, or a full dropping queue. Processing happens later. Event names and invalid arguments can still throw; inspect statistics and logs for ingestion outcomes.
Input is copied before queuing. Reuse it after Track returns or the asynchronous call completes. AnalyticsEvent supports name, timestamp, site, visitor ID, URL/referrer context, dimensions, values, hash routing, and non-interactive custom events.
Queries and discovery
| Pair | Result |
|---|---|
| Query / QueryAsync with AnalyticsQuery | AnalyticsResult |
| Query / QueryAsync with FunnelQuery | FunnelResult |
| GetEventNames / GetEventNamesAsync | Event names over the selected range and site |
| Web.GetSummary / GetSummaryAsync | WebSummaryReport |
| Web.GetTimeseries / GetTimeseriesAsync | Time-series report |
| Web.GetBreakdown / GetBreakdownAsync | Dimension breakdown |
| Web.GetPages / GetPagesAsync | Page report |
| Web.GetExitPages / GetExitPagesAsync | Exit-page report |
| Web.GetGoals / GetGoalsAsync | Custom-action report |
| Web.GetBots / GetBotsAsync | Separate bot report |
| Web.GetCurrentVisitors / GetCurrentVisitorsAsync | Nullable recent visitor count |
Storage-reading asynchronous forms run the work on a thread-pool thread. They do not change query semantics. Pass a cancellation token for work tied to a request.
Maintenance and privacy
| Pair | Contract |
|---|---|
| Flush / FlushAsync | Drain prior accepted work and write buffered aggregates and session state |
| Compact / CompactAsync | Flush and merge open stores toward disk |
| ApplyRetention / ApplyRetentionAsync | Run physical retention maintenance |
| EraseVisitor / EraseVisitorAsync | Remove an explicit visitor's retained raw events, index copies, and session |
| Dispose / DisposeAsync | Stop ingestion, drain accepted work, flush, and release ownership |
Erasure returns deleted raw-event count and does not subtract already aggregated rollup history. Compaction is useful after bulk imports but is separate from ordinary periodic flushing.
Statistics
GetStatistics() includes partition information. GetStatistics(includePartitions:false) gives lightweight counters without inspecting those stores.
| Group | Fields |
|---|---|
| Ingestion | EventsAccepted, EventsProcessed, EventsDropped, EventsRejected, EventsFiltered |
| Health | QueueLength, Errors, Flushes |
| Web activity | SessionsStarted, BotsExcluded, BotsTracked |
| Storage | OpenPartitions, PartitionsDropped, Partitions |
Partition record counts are approximate and may be null for partitions not opened since startup. Counters start again with a new engine instance.
Errors and cancellation
AnalyticsException is the base for engine-specific errors. AnalyticsConfigurationException reports incompatible options or dataset configuration. AnalyticsQueryException reports unanswerable or bounded queries; AnalyticsQueryTimeoutException is its timeout subclass.
Invalid arguments use standard argument exceptions. Caller cancellation uses the cancellation token; configured query timeout additionally protects expensive report execution.
Advanced storage access
analytics.Storage exposes the actual ZoneTrees and their existing maintainers. Raw events and rollups are partitioned: list the partition names, then acquire a disposable lease. Acquiring a closed partition opens it; listing names does not. A lease keeps the partition open and defers retention deletion until it is released.
using ZoneTree.Analytics.Storage;
var raw = analytics.Storage.RawEvents; // null when raw event storage is disabled
if (raw != null)
{
foreach (var name in raw.GetPartitions())
{
using var partition = await raw.AcquireAsync(name);
if (partition == null) continue; // retention removed it after the list was read
var tree = partition.ZoneTree; // IZoneTree<Memory<byte>, Memory<byte>>
var maintainer = partition.Maintainer; // the engine's existing IMaintainer
using var iterator = tree.CreateIterator();
while (iterator.Next())
{
var stored = RawEventCodec.Decode(iterator.CurrentKey.Span, iterator.CurrentValue.Span);
if (stored == null) continue; // deletion marker
Console.WriteLine($"{stored.Site}: {stored.Timestamp:u} {stored.Name}");
if (stored.TryGetDimension("page", out var page)) Console.WriteLine(page);
}
}
}
var hourly = analytics.Storage.GetRollups(Granularity.Hour); // null when disabled
// hourly.Acquire(name).ZoneTree is IZoneTree<Memory<byte>, RollupValue>.
var sessions = analytics.Storage.Sessions; // IZoneTree<ulong, SessionState>, null when disabled
var sessionsMaintainer = analytics.Storage.SessionsMaintainer; Acquire and AcquireAsync accept a cancellation token and return null for a missing partition; they never create partitions. GetRollups takes exactly one of Minute, Hour or Day. RollupValue, SessionState and the rollup statistics and sketches are public for typed access; public codecs interpret encoded keys and raw event values without requiring a serializer or knowledge of byte offsets.
Storage codecs
RawEventCodec.Decode(key, value) returns a RawEvent with its site, UTC timestamp, sequence, event name, visitor hash, session information, dimensions and measured values. Use TryGetDimension and TryGetValue for named lookups. An empty value decodes to null (a deletion). RawEventCodec.EncodeKey(record) and Encode(record) convert it back to stored bytes; Encode(null) produces a deletion marker.
StorageKeyCodec.DecodeRawEvent(key) decodes just the raw key, while DecodeRollup(key) returns the rollup's site, event, bucket and a dictionary of named dimensions. Missing dimensions decode to null and explicitly empty values to "". EncodeRawEvent(key) and EncodeRollup(key) convert the decoded keys back to bytes, ordering rollup dimensions canonically.
var hourly = analytics.Storage.GetRollups(Granularity.Hour);
if (hourly != null)
{
foreach (var name in hourly.GetPartitions())
{
using var partition = await hourly.AcquireAsync(name);
if (partition == null) continue;
using var iterator = partition.ZoneTree.CreateIterator();
while (iterator.Next())
{
var key = StorageKeyCodec.DecodeRollup(iterator.CurrentKey.Span);
var bucketStart = key.GetBucketStart(Granularity.Hour, analytics.TimeZone);
Console.WriteLine($"{bucketStart:u}: {key.Site} / {key.Event}");
}
}
} The rollup key does not contain its granularity: pass the tier's granularity and the engine's time zone to GetBucketStart. Minute and hour buckets store UTC ticks; daily buckets store a local calendar date. Session keys and values are already typed as ulong and SessionState. Codecs preserve stored data without applying ingestion normalization. Encoders reject invalid Unicode and out-of-range event or session dates rather than silently replacing text or producing unreadable records. DefaultSite must also contain valid Unicode and no control characters. Each decoded raw event keeps only its own data, without a scan cache. Malformed encoded keys and raw event payloads raise FormatException.
Release leases before disposing the engine. The engine owns all trees and maintainers: do not dispose or drop them, or create another maintainer. A lease does not stop ingestion, compaction or record expiration. Treat stored values, arrays and sketches as immutable, and construct replacement values for writes. Direct writes bypass ingestion and aggregation and can make raw events, rollups and cached sessions inconsistent. The metadata store uses files rather than ZoneTree and is not exposed.
For shared indexes, restrict iterators using the returned logical KeyPrefix. Each accessor exposes a logical namespace within the shared physical store.