ClientStats

Struct ClientStats 

Source
pub struct ClientStats {
    pub total_bloops: u64,
    pub per_minute_bloops: [u8; 1440],
    pub per_minute_dates: [NaiveDate; 1440],
    pub bloops_per_hour: [u32; 24],
    pub bloops_per_day: HashMap<NaiveDate, u32>,
}
Expand description

Stats collected per client to track “bloops” over time.

This structure holds aggregated counters for the last 24 hours and beyond, to efficiently support queries like “how many bloops in the last hour” or “bloops per day”. It is designed to be seeded from a database on startup.

§Seeding from PostgreSQL

To efficiently load these stats from a Postgres database with potentially thousands of bloops per client, the following SQL queries are recommended.

CREATE INDEX idx_bloops_client_recorded_at ON bloops (
    client_id,
    recorded_at
);

This index speeds up queries filtering by client and time range.

§Total bloops per client:

SELECT client_id, COUNT(*) as total_bloops
FROM bloops
GROUP BY client_id;

§Bloops per minute for the last 24 hours (UTC):

SELECT client_id,
       DATE_TRUNC(
           'minute',
           recorded_at AT TIME ZONE 'UTC'
       ) AS minute_bucket,
       COUNT(*) AS count
FROM bloops
WHERE recorded_at >= NOW() - INTERVAL '24 hours'
GROUP BY client_id, minute_bucket;
  • minute_bucket contains the UTC timestamp truncated to the minute.
  • You can map minute_bucket to your in-memory bucket index like this:

§Mapping to In-Memory Buckets:

You can map the minute_bucket to your in-memory buckets like this:

use chrono::{NaiveDateTime, Timelike};
use bloop_server_framework::statistics::{minute_index, ClientStats};
use bloop_server_framework::test_utils::Utc;

// Load these from your database
let minute_bucket = Utc::now().naive_utc();
let count = 0;

let mut client_stats = ClientStats::new();
let idx = minute_index(minute_bucket);
let date = minute_bucket.date();

client_stats.per_minute_dates[idx] = date;
client_stats.per_minute_bloops[idx] = count as u8;

§Bloops per hour for the last 24 hours (in local time):

SELECT client_id,
       EXTRACT(hour FROM recorded_at AT TIME ZONE 'your_timezone') AS hour,
       COUNT(*) AS count
FROM bloops
WHERE recorded_at >= NOW() - INTERVAL '24 hours'
GROUP BY client_id, hour;

§Bloops per day (in local time):

SELECT client_id,
       DATE(recorded_at AT TIME ZONE 'your_timezone') AS day,
       COUNT(*) AS count
FROM bloops
GROUP BY client_id, day;

After executing these queries, aggregate the counts per client into the fields of this struct.

§Example usage:

use std::collections::HashMap;
use bloop_server_framework::statistics::ClientStats;

let mut stats_map: HashMap<String, ClientStats> = HashMap::new();

// For each row of minute-buckets query:
// 1. Parse `minute_bucket` into NaiveDate and index.
// 2. Insert count into `per_minute_bloops` and `per_minute_dates`.

// Similarly for other queries...

Fields§

§total_bloops: u64

Total bloops observed for this client.

§per_minute_bloops: [u8; 1440]

Count of bloops per minute in a rolling 24-hour buffer.

Indexed by minute of day (0..1439).

§per_minute_dates: [NaiveDate; 1440]

The date associated with each minute bucket, used to invalidate stale data.

§bloops_per_hour: [u32; 24]

Count of bloops per hour in the local timezone.

§bloops_per_day: HashMap<NaiveDate, u32>

Count of bloops per day, keyed by date in local timezone.

Implementations§

Source§

impl ClientStats

Source

pub fn new() -> Self

Creates a new empty statistics struct.

Trait Implementations§

Source§

impl Debug for ClientStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClientStats

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Erasable for T

Source§

const ACK_1_1_0: bool = true

Whether this implementor has acknowledged the 1.1.0 update to unerase’s documented implementation requirements. Read more
Source§

unsafe fn unerase(this: NonNull<Erased>) -> NonNull<T>

Unerase this erased pointer. Read more
Source§

fn erase(this: NonNull<Self>) -> NonNull<Erased>

Turn this erasable pointer into an erased pointer. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more