KennethWilke / tenorite

A rust library for concurrent greatness
MIT License
3 stars 1 forks source link

RFD: Task returns, workers gone awry and handy abstractions #2

Open KennethWilke opened 2 years ago

KennethWilke commented 2 years ago

Looking to improve a few things and gather wider feedback based on suggestions from @moosingin3space

Task returns

The first idea is around getting a return type or error out of a worker thread. The idea being that if a worker task completes, or bubbles up an error, you can get to that result. My current inclination is to add another generic for the task return type:

diff --git a/src/worker.rs b/src/worker.rs
index 0c45c1c..0931007 100644
--- a/src/worker.rs
+++ b/src/worker.rs
@@ -7,7 +7,7 @@ use crate::request::TenoriteRequest;
 /// This trait specifies the worker end of the service. the `task()` method is
 /// what implements the server side logic.
 #[async_trait]
-pub trait TenoriteWorker<Request, Response, Error, TaskConfig>
+pub trait TenoriteWorker<Request, Response, Error, TaskConfig, TaskResult>
     where Request: Debug,
           Response: Debug,
           Error: Debug,
@@ -16,5 +16,5 @@ pub trait TenoriteWorker<Request, Response, Error, TaskConfig>
     async fn task(
         mut receiver: Receiver<TenoriteRequest<Request, Response, Error>>,
         config: TaskConfig,
-    );
+    ) -> TaskResult;
 }

This feels like a pretty simple and permissive option. Currently returns aren't supported so the JoinHandle wraps the () type. This would instead use the generic JoinHandle<TaskResult>

Another alternative that came up was having an abstracted handler structure returned that has some additional channels for errors or some other kind of control plane specific use

Workers gone awry

Worker panic handling or better reactions if a service's 💩 hits the fan. This would be nice but I don't currently have any good solutions

More concurrent concurrency

There are probably a few other good async message patterns that can be similarly abstracted. The core tokio channel types are simple enough to use but probably other good models or components are waiting to be found. I don't have any concrete ideas but some thoughts are:

Cleaner Implementation

I'm hopeful there's also a way to simplify all the generics clutter when implementing a service. I've been experimenting with macros and maybe there's a good pattern there for simplifying the process of getting all the right types where they need to go without being an eyesore with minimal cognitive load.

Alternatively, maybe another layer can be used, some type of container for a group of related generic types.

Assumptions check

A few trade-offs were made to achieve a nice usage pattern, and some core choices were hastily made to quiet that pesky compiler with all its helpful advice. Sure cargo, it's all 'static and Send or Debug or whatever you want from me! I like this usage pattern so I'll just give you all the trait bounds you want!!

This mostly happened in TenoriteService, but if anyone finds these needlessly restricting or has other assumptions to challenge I'm very open to suggestions on that front

KennethWilke commented 2 years ago

Added the TaskResult change to the development branch: https://github.com/KennethWilke/tenorite/commit/79a1d4a5fd64cc17f7e98b07f63069b219b9495a