Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul stats: Implement a NoOpSender sender #1343

Closed
josecelano opened this issue Mar 3, 2025 · 2 comments
Closed

Overhaul stats: Implement a NoOpSender sender #1343

josecelano opened this issue Mar 3, 2025 · 2 comments
Assignees
Labels
- Developer - Torrust Improvement Experience Code Cleanup / Refactoring Tidying and Making Neat

Comments

@josecelano
Copy link
Member

There are some functions like this:

async fn send_stats_event(&self, peer_ip: IpAddr) {
    if let Some(http_stats_event_sender) = self.opt_http_stats_event_sender.as_deref() {
        match peer_ip {
            IpAddr::V4(_) => {
                http_stats_event_sender
                    .send_event(statistics::event::Event::Tcp4Announce)
                    .await;
            }
            IpAddr::V6(_) => {
                http_stats_event_sender
                    .send_event(statistics::event::Event::Tcp6Announce)
                    .await;
            }
        }
    }
}

We can simplify those functions:

async fn send_stats_event(&self, peer_ip: IpAddr) {
    if let Some(http_stats_event_sender) = self.opt_http_stats_event_sender.as_deref() {
        let event = match peer_ip {
            IpAddr::V4(_) => statistics::event::Event::Tcp4Announce
            IpAddr::V6(_) => statistics::event::Event::Tcp6Announce
        }
        http_stats_event_sender.send_event(event).await;
    }
}

And we simplify even further if we implement a NoOpSender. Instead of injecting an optional event sender (opt_http_stats_event_sender) in the constructor we can inject a null sender.
The null sender does nothing.

Factory functions can return always a "real" sender or the NoOpSender:

pub fn factory(
    tracker_usage_statistics: bool,
) -> (
    Option<Box<dyn statistics::event::sender::Sender>>,
    statistics::repository::Repository,
) {
    let mut stats_event_sender = None;

    let mut stats_tracker = statistics::keeper::Keeper::new();

    if tracker_usage_statistics {
        stats_event_sender = Some(stats_tracker.run_event_listener());
    }

    (stats_event_sender, stats_tracker.repository)
}

Alternatively:

#[derive(Debug, Default)]
pub struct NoOpSender {}

impl Sender for NoOpSender {
    fn send_event(&self, _event: Event) -> BoxFuture<'_, Option<Result<(), SendError<Event>>>> {
        async move { None }.boxed()
    }
}

#[must_use]
pub fn factory(
    tracker_usage_statistics: bool,
) -> (
    Option<Box<dyn statistics::event::sender::Sender>>,
    statistics::repository::Repository,
) {
    let mut stats_tracker = statistics::keeper::Keeper::new();

    let stats_event_sender = if tracker_usage_statistics {
        Some(stats_tracker.run_event_listener())
    } else {
        let no_op_sender: Box<dyn Sender> = Box::new(NoOpSender::default());
        Some(no_op_sender)
    };

    (stats_event_sender, stats_tracker.repository)
}

This was the sender is always available and we can remove the "if" condition from:

async fn send_stats_event(&self, peer_ip: IpAddr) {
    let event = match peer_ip {
        IpAddr::V4(_) => statistics::event::Event::Tcp4Announce
        IpAddr::V6(_) => statistics::event::Event::Tcp6Announce
    }
    http_stats_event_sender.send_event(event).await;
}
@josecelano
Copy link
Member Author

IMPORTANT!: Please read this comment before implementing this issue.

If events are always enabled this issue does not make sense.

@josecelano
Copy link
Member Author

IMPORTANT!: Please read this comment before implementing this issue.

If events are always enabled, this issue does not make sense.

I think we should be able to:

  • Enable/disable events
  • Enable/disable stats

Independently.

In some cases, where only performance is crucial and the user does not care about stats, it could be convenient to disable events and stats altogether.

The NoOpSender is not a good idea because if we always enable events (even when we don't want to process them), the performance could be similar with the current events (there are simple enums). However, I plan to enrich the events. With "fat" events, the NoOpSender would have the cost of building the event. That requires copying objects in memory.

NOTICE: Enabling stats requires enabling events, but we can check that dependency while checking the loading configuration. We have other similar checks.

cc @da2ce7

@josecelano josecelano changed the title Overhaul stats events: Implement a NoOpSender sender Overhaul stats: Implement a NoOpSender sender Mar 12, 2025
@josecelano josecelano self-assigned this Mar 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- Developer - Torrust Improvement Experience Code Cleanup / Refactoring Tidying and Making Neat
Projects
None yet
Development

No branches or pull requests

1 participant