Skip to main content

Module stream

Module stream 

Source
Expand description

One supervised gRPC server-stream: open, read, reopen with backoff.

A stream task only reads and forwards frames. Parsing into events happens in the pump. Closing a stream (dropping it) is the protocol-level unsubscribe for gRPC (RST_STREAM).

What the supervisor does:

  • Opens the stream after taking quota for it (opening counts as a request) and within a deadline.
  • Reopens it after a break or a normal end, with a pause that doubles from 0.5 s to 30 s. A stream that lived at least 10 s counts as healthy and the pause starts over.
  • Asks for a new token when the broker answers UNAUTHENTICATED.
  • Treats some refusals as permanent (INVALID_ARGUMENT, NOT_FOUND, PERMISSION_DENIED, UNIMPLEMENTED, FAILED_PRECONDITION): it reports Failed once and keeps trying slowly (every 30 s), because a permission may be granted later.
  • Reports every step as a StreamEvent through the wrap function.

§Examples

use std::sync::Arc;
use std::time::Duration;
use brokapi_core::SubscriptionId;
use brokapi_finam::core::StreamEvent;
use brokapi_finam::limits::default_limits;
use brokapi_finam::stream::{OpenFn, StreamParams, spawn_stream};
use brokapi_runtime::BrokerLimiter;
use brokapi_finam_protocol::pb::grpc::tradeapi::v1::marketdata as pm;
use brokapi_finam_protocol::{ApiEndpoint, FinamApi, start_token_manager};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;

let tokens = start_token_manager(&endpoint, SECRET, "").await?;
let api = FinamApi::connect(&endpoint, Arc::clone(&tokens), false)?;

// How to open the stream. The supervisor calls it again after every reconnect.
let open: OpenFn<pm::SubscribeOrderBookResponse> = Arc::new(move || {
    let api = api.clone();
    Box::pin(async move {
        let request = pm::SubscribeOrderBookRequest { symbol: "SBER@MISX".into() };
        api.market_data().subscribe_order_book(request).await.map(|r| r.into_inner())
    })
});

let (tx, mut frames) = mpsc::channel(16);
let handle = spawn_stream(
    StreamParams {
        sub: SubscriptionId::new(1),
        method: "SubscribeOrderBook",
        limiter: BrokerLimiter::new(&default_limits()),
        tokens,
        open,
        open_timeout: Duration::from_secs(5),
        wrap: Arc::new(|event| event), // keep the events as they are
        tx,
        first: None,
    },
    CancellationToken::new(),
);

let first = tokio::time::timeout(Duration::from_secs(5), frames.recv()).await?.unwrap();
assert!(matches!(first, StreamEvent::Connected));

assert!(handle.stop(Duration::from_secs(2)).await);

Structs§

StreamHandle
Handle of a running stream supervisor: restart it, stop it, or abort it.
StreamParams
Everything spawn_stream needs. M is the message type of the stream, F the frame type the connector wants.

Functions§

spawn_stream
Starts the supervisor task. cancel should be a child of the connector’s root token.

Type Aliases§

FirstOutcome
Result of the first open attempt, reported once to subscribe_*.
OpenFn
How to open a stream: a function that returns the future of a new stream. Called again after every drop, so it must build a fresh request each time. It reads the token through the FinamApi it captured.