pub struct MockSuotarStore {
client: Client,
connection: OnceCell<ConnectionManager>,
generation: RwLock<Option<String>>,
install_lock: Mutex<()>,
}Fields§
§client: Client§connection: OnceCell<ConnectionManager>§generation: RwLock<Option<String>>Cached because one server process owns the index; re-read only when a prefixed read comes back empty.
install_lock: Mutex<()>Implementations§
Source§impl MockSuotarStore
impl MockSuotarStore
Sourcepub fn new(redis_url: &str, db_index: i64) -> Result<Self>
pub fn new(redis_url: &str, db_index: i64) -> Result<Self>
Swaps in the mock’s own database index so a flush touches nothing of the cache’s. Connects on first use, so an unreachable Redis is a per-request error rather than a cached success.
async fn conn(&self) -> Result<ConnectionManager>
fn cached_generation(&self) -> Option<String>
fn cache_generation(&self, generation: Option<String>)
pub async fn live_generation(&self) -> Result<Option<String>>
Sourcepub async fn install_world(
&self,
world: &World,
db_generation: Option<&str>,
) -> Result<String>
pub async fn install_world( &self, world: &World, db_generation: Option<&str>, ) -> Result<String>
Flips the generation pointer last, so no request sees a half-installed world and a push needs nothing cleared before it.
Sourcepub async fn install_if_absent(
&self,
world: &World,
db_generation: Option<&str>,
) -> Result<String>
pub async fn install_if_absent( &self, world: &World, db_generation: Option<&str>, ) -> Result<String>
Serialised so a burst of first requests against an empty index does not each mint a generation.
async fn has_world(&self, generation: &str) -> Result<bool>
Sourcepub async fn flush(&self) -> Result<()>
pub async fn flush(&self) -> Result<()>
Safe because the index is the mock’s alone; the next contract request builds the world lazily.
pub async fn preamble(&self, generation: &str) -> Result<Preamble>
pub async fn load_persons( &self, generation: &str, student_numbers: &[String], ) -> Result<BTreeMap<String, MockPerson>>
pub async fn load_product_tokens( &self, generation: &str, product_ids: &[String], ) -> Result<BTreeMap<String, MockProductAccessToken>>
Sourcepub async fn load_for_person_course(
&self,
generation: &str,
student_numbers: &[String],
course_codes: &[String],
) -> Result<WorkingSet>
pub async fn load_for_person_course( &self, generation: &str, student_numbers: &[String], course_codes: &[String], ) -> Result<WorkingSet>
Two pipelined round trips whatever the batch size: the keyed hashes, then the entities they point at.
Sourcepub async fn load_for_verify(
&self,
generation: &str,
submitted_attainment_ids: &[String],
) -> Result<WorkingSet>
pub async fn load_for_verify( &self, generation: &str, submitted_attainment_ids: &[String], ) -> Result<WorkingSet>
Verify’s body carries only submitted attainment ids, so the persons behind them come second.
pub async fn load_for_list_by_course( &self, generation: &str, course_codes: &[String], ) -> Result<WorkingSet>
Sourcepub async fn commit(
&self,
generation: &str,
working: &WorkingSet,
call: &RecordedCall,
call_log_capacity: usize,
) -> Result<()>
pub async fn commit( &self, generation: &str, working: &WorkingSet, call: &RecordedCall, call_log_capacity: usize, ) -> Result<()>
The one write of a request: changed entities plus its call-log entry, in one atomic pipeline.
pub async fn next_call_seq(&self, generation: &str) -> Result<u64>
pub async fn next_person_seq(&self, generation: &str) -> Result<i64>
Sourcepub async fn next_fault_seq(&self, generation: &str) -> Result<u64>
pub async fn next_fault_seq(&self, generation: &str) -> Result<u64>
Re-arming an id takes a fresh one, so the fault moves to the back of arm order.
Sourcepub async fn draw(
&self,
generation: &str,
fault_id: &str,
delta: i64,
) -> Result<i64>
pub async fn draw( &self, generation: &str, fault_id: &str, delta: i64, ) -> Result<i64>
The caller acts on the returned value, never on a separate read.
pub async fn upsert_json<T: Serialize>( &self, generation: &str, hash: EntityHash, entries: &BTreeMap<String, T>, ) -> Result<()>
pub async fn get_json<T: DeserializeOwned>( &self, generation: &str, hash: EntityHash, field: &str, ) -> Result<Option<T>>
pub async fn all_json<T: DeserializeOwned>( &self, generation: &str, hash: EntityHash, ) -> Result<BTreeMap<String, T>>
pub async fn delete_fields( &self, generation: &str, hash: EntityHash, fields: &[String], ) -> Result<()>
pub async fn owner_keys( &self, generation: &str, field: &str, ) -> Result<Option<OwnerKeys>>
pub async fn known_owner_refs(&self, generation: &str) -> Result<Vec<String>>
pub async fn faults(&self, generation: &str) -> Result<Vec<Fault>>
pub async fn remaining_budgets( &self, generation: &str, ) -> Result<HashMap<String, i64>>
pub async fn arm_fault(&self, generation: &str, fault: &Fault) -> Result<()>
pub async fn disarm_faults( &self, generation: &str, ids: &[String], ) -> Result<()>
Sourcepub async fn recent_calls(
&self,
generation: &str,
limit: usize,
) -> Result<Vec<RecordedCall>>
pub async fn recent_calls( &self, generation: &str, limit: usize, ) -> Result<Vec<RecordedCall>>
Every read of the call log is bounded: a list has no index, so a filter is always a scan.
pub async fn counts(&self, generation: &str) -> Result<WorldCounts>
pub async fn set_defaults( &self, generation: &str, defaults: &WorldDefaults, ) -> Result<()>
pub async fn clear_hash(&self, generation: &str, hash: EntityHash) -> Result<()>
pub async fn clear_faults(&self, generation: &str) -> Result<()>
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for MockSuotarStore
impl !RefUnwindSafe for MockSuotarStore
impl Send for MockSuotarStore
impl Sync for MockSuotarStore
impl Unpin for MockSuotarStore
impl UnsafeUnpin for MockSuotarStore
impl !UnwindSafe for MockSuotarStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].