{"id":17951202,"url":"https://github.com/mandel-macaque/marille","last_synced_at":"2025-10-03T22:55:56.622Z","repository":{"id":241424767,"uuid":"804926734","full_name":"mandel-macaque/marille","owner":"mandel-macaque","description":"In-app publisher/subscriber .Net library","archived":false,"fork":false,"pushed_at":"2025-02-17T01:48:47.000Z","size":295,"stargazers_count":21,"open_issues_count":4,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-28T21:25:44.292Z","etag":null,"topics":["api","channels","dotnet","hub","threading"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mandel-macaque.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-23T14:39:03.000Z","updated_at":"2025-04-15T16:30:07.000Z","dependencies_parsed_at":"2024-09-12T07:47:26.040Z","dependency_job_id":"ec4da42d-92ea-4663-a700-238146408007","html_url":"https://github.com/mandel-macaque/marille","commit_stats":null,"previous_names":["mandel-macaque/marille"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/mandel-macaque/marille","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandel-macaque%2Fmarille","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandel-macaque%2Fmarille/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandel-macaque%2Fmarille/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandel-macaque%2Fmarille/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mandel-macaque","download_url":"https://codeload.github.com/mandel-macaque/marille/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandel-macaque%2Fmarille/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278239967,"owners_count":25954097,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-03T02:00:06.070Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","channels","dotnet","hub","threading"],"created_at":"2024-10-29T09:44:16.993Z","updated_at":"2025-10-03T22:55:56.593Z","avatar_url":"https://github.com/mandel-macaque.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# marille\nMarille is thin layer on top of System.Threading.Channels that provides a in-process publisher/subscriber hub.\n\nThe aim of the library is to simplify the management of several `Channels\u003cT\u003e` within an application. The general idea\nis that users can create different \"topics\" that will later be divided in subtopics based on the type of\nmessages that they distribute. The normal pattern for usage is as follows:\n\n1. Create a topic, which is a pair of a topic name and a type of messages.\n2. Add 1 or more workers which will be used to consume events.\n3. Use the Hub to publish events to a topic.\n4. Workers will pick the messages and consume them based on the topic strategy that was chosen.\n\nYou can think about Marille as an in-process pub/sub similar to a distributed queue with the difference\nthat the constraints are much simpler. Working within the same process makes dealing with failure much easier.\n\nThe library does not impose any thread usage and it fully relies on the default [TaskScheduler](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-threading-tasks-taskscheduler) used in \nthe application.\n\n## Examples\n\nHere are some examples of the API usage:\n\n### Single Publisher/Worker\n\nIn this pattern we will create a single topic with a unique consumer. The Hub will only send messages to the registered\nconsumer. \n\n1. Declare your consumer implementation:\n\n```csharp\n// define a message type to consumer\npublic record struct MyMessage (string Id, string Operation, int Value);\n\n// create a worker type with the minimum implementation \nclass MyWorker : IWorker\u003cMyMessage\u003e {\n    \n    // Workers will be by default considered to be IO bound unless specified otherwise\n    public bool UseBackgroundThread =\u003e false;\n\n    public Task ConsumeAsync (MyMessage message, CancellationToken cancellationToken = default)\n       =\u003e Task.FromResult (Completion.TrySetResult(true));\n}\n\n// creeate an error worker to handle exceptions from workers\nclass ErrorWorker : IErrorWorker\u003cMyMessage\u003e {\n    public bool UseBackgroundThread =\u003e false;\n    \n    public Task ConsumeAsync (MyMessage message, Exception exception, CancellationToken cancellationToken = default)\n        =\u003e Task.FromResult (Completion.TrySetResult(true));\n}\n\n```\n\n2. Setup the topic via a new Hub:\n\n```csharp\n\nprivate Hub _hub;\n\npublic Task CreateChannels () {\n    _hub = new Hub ();\n    // configuration to be used to create the topic\n    var configuration = new() { Mode = ChannelDeliveryMode.AtMostOnce };\n    var topic = \"topic\";\n    var worker = new MyWorker ();\n    var errorWorker = new ErrorWorker ();\n    // workers can be either added during the channel creation or after the fact\n    // with the TryRegister method.\n    return _hub.CreateAsync (topic, configuration, errorWorer, worker);\n}\n```\n\nLambdas can also be registered as workers:\n\n```csharp\nFunc\u003cMyMessage, CancellationToken, Task\u003e worker = (_, _) =\u003e Task.FromResult (true);\nreturn _hub.CreateAsync (topic, configuration, worker);\n```\n\n3. Publish messages via the hub.\n\n```csharp\npublic Task ProduceMessage (string id, string operation, int value) =\u003e\n    _hub.Publish (topic, new MyMessage (di, operation, value));\n```\n\n4. Cancel and wait for events to be completed\n\nBecasue the entire library puspose of the library is to be able to process \nevents in a multithreaded manner, the main thread has to wait until the events\nare processed. That can be done by waiting on a Channel to be closed:\n\n```csharp\n// close a specific topic\nawait _hub.CloseAsync\u003cMyMessage\u003e (topic);\n\n// close all topcis\nawait _hub.CloseAsync\u003cMyMessage\u003e (topic1);\n```\n\n5. Error handling\n\nThe library provides a way to handle exceptions that are thrown by the workers. The error worker is called\nwhenever an exception is thrown by a worker that is consuming a topic. It is up to the implementation to decide \nif the message should be retried or not. \n\nIf we do not want to retry a message a worker can throw and exception, such exception will be added to a queue that \nwill be consumed by the error worker that was used when the topic was created. Each topic has its own error queue, that\nmeans that the error worker will only consume errors from the topic that it was created for.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmandel-macaque%2Fmarille","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmandel-macaque%2Fmarille","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmandel-macaque%2Fmarille/lists"}