Raw capture โ raw/redb-330-enterprise-net-stack.md
url: https://dev.to/rinat_kozin/redb-330-an-enterprise-net-stack-you-actually-own-typed-store-a-homegrown-apache-camel-and-3gd1 title: "redb 3.3.0: an enterprise .NET stack you actually own โ typed store, a homegrown Apache Camel, and a runtime with a dashboard (all free)" author: Rinat Kozin date_fetched: 2026-07-18 date_published: 2026-07-09
Author: Rinat Kozin (creator of the redb ecosystem) Published: July 9, 2026 on dev.to Tags: #dotnet #opensource #llm #sqlite GitHub: github.com/redbase-app | Site: redb.ru
redb โ A typed store for .NET. Write a POCO, tag it with an attribute, use full LINQ server-side with no migrations and no Include. Providers: Postgres, MSSQL, SQLite. Free and Pro editions (change tracking, bulk, cache, analytics).
redb.Route โ An integration engine in the Apache Camel spirit: route DSL (From(...)...To(...)), 30+ connectors (Kafka, RabbitMQ, HTTP, gRPC, S3, LLM, etc.), enterprise integration patterns, transactions, telemetry.
redb.Tsak โ A runtime that turns routes into a production service: dashboard, hot-reloadable modules (.tpkg), live context management, metrics, cluster with coordinator and failover. Ships as NuGet packages, Docker images, and standalone archives.
[RedbScheme]
public class Note
{
public string Tag { get; set; } = "";
public string Text { get; set; } = "";
}
// write
await redb.SaveAsync(new RedbObject<Note> { Props = new() { Tag = "work", Text = "hello" } });
// read โ server-side query
var notes = await redb.Query<Note>()
.Where(n => n.Tag == "work")
.ToListAsync();
Schema comes from SyncSchemeAsync<Note>() โ generated off the class itself, no migration files.
redb.Route.Sqs)One package, two schemes: sqs:// (queue) and sns:// (topic). Built on the native AWS SDK for .NET v4.
// Consumer
From(Sqs.Queue("orders").WaitTimeSeconds(20).ConcurrentConsumers(5))
.Process(ex => Handle(ex.In.Body));
// Publisher
From("timer://tick?period=5000")
.SetBody(_ => BuildEvent())
.To(Sns.Topic("order-events"));
Features: long-polling, real competing consumer loops, visibility timeout extension, transactional ack via .Transacted(), FIFO support, SNSโSQS auto-subscription. Compatible with LocalStack/ElasticMQ. Full AWS credential chain honored; W3C trace context across hops.
redb.Route.Telegram)The telegram:// scheme, built on Telegram.Bot. Consumer is a long-poller (single getUpdates stream per token); producer covers send/document/photo/edit/delete/answer.
// Echo bot
From(Tg.Receive(token))
.Process(ex => ex.Out.Body = $"You said: {ex.In.Body}")
.To(Tg.Send(token).ChatId(Header(TelegramHeaders.ChatId)));
Features: honors 429 retry_after contract, validates parseMode, supports inline/reply keyboards, webhook-unpack pipeline (UnpackTelegramUpdate), fluent DSL. Delivery is at-most-once (Telegram advances update offset upon handing it to you). Parallelism via .Threads(N).
knowledge://, embed://, and .Knowledge()Three new pieces that close the RAG loop:
knowledge:// (ingest scheme): Takes message body as a document, chops into chunks (deterministic character windows with overlap), embeds each if an IEmbeddingProvider is registered, upserts into IKnowledgeStore.
From("file://docs?include=*.md").To("knowledge://handbook");
Chunk IDs follow {docId}#{index} format โ re-ingesting replaces chunks in place.
embed:// (embeddings step): The mirror of llm:// โ turns message body text (or batch) into a vector on Out.Body. URI host names a connection factory.
.Knowledge(collection, k) (retrieval DSL step): Pulls top-K chunks for current message and "injects them into the system prompt" so the next .To("llm://โฆ") answers grounded on them.
From("kafka://questions")
.Knowledge("handbook", k: 5)
.To("llm://claude");
Semantic search when IEmbeddingProvider is present (cosine SearchAsync), keyword otherwise (SearchTextAsync via server-side LIKE). No store wired = no-op, never breaks pipeline.
knowledge_search (ready-made tool): An .AsLlmTool route โ input {query, top_k?, collection?} โ {results:[โฆ]}. Collection can be pinned to fence an agent.
Notable fix: Switched to UnsafeRelaxedJsonEscaping across LLM serializers โ previously non-ASCII characters were escaped as \uXXXX, roughly 6ร'ing tokens for Cyrillic/CJK content and breaking keyword LIKE matching in the knowledge store.
.Threads(N) โ Concurrency on Any SourceA processing-concurrency stage that caps a route section at N parallel workers โ no named seda:// endpoint required.
From("mqtt://sensors")
.Threads(8)
.Process(ex => HeavyWork(ex))
.EndThreads();
Adaptive to exchange pattern: for InOnly (fire-and-forget) it's a hand-off with clone + worker pool; for InOut it runs inline on the same exchange under a SemaphoreSlim gate so the reply survives. Supports .MaxQueueSize(n) and .EnqueueTimeout(TimeSpan). Ordering not preserved at N > 1.
ConcurrentConsumers(N) Finally Works"
ConcurrentConsumers(N)was silently giving you no parallelism on the brokers."
The option existed and sized a SemaphoreSlim โ but no actual concurrency resulted.
Per connector:
RabbitMQ: Channel was created with CreateChannelOptions(...) leaving consumerDispatchConcurrency at its compile-time default of 1 (in RabbitMQ.Client 7.2.1), which overrode the connection-level setting. Now channels open with explicit dispatch concurrency matching ConcurrentConsumers. (Fix in 3.2.2.)
Kafka: New EnableAutoCommit option (default true). Fix for transacted producer throwing Local: Erroneous state on deferred send. (3.2.1.)
AMQP 1.0 and IBM MQ: Serial receive loop awaited Process inline before pulling next message โ SemaphoreSlim was dead. Now N genuine competing consumers each with own session/connection. IBM MQ fix also addresses connection-scoped syncpoint: "Backout() rolls back the whole connection, so each worker must own its own." Topics clamped to single subscriber with warning. (3.2.1.)
Upgrade warning: If a route had ConcurrentConsumers(N > 1) and "worked," it was serial. After upgrade it genuinely parallelizes โ "per-queue ordering is no longer preserved" and handlers must be thread-safe. Routes at default 1 are untouched.
IRedbService wraps one non-thread-safe DB connection. DSL steps like ProcessWithRedb(...), SetBodyFromRedb(...), SetHeaderFromRedb(...), and BeginRedbTransaction() previously used a "single IRedbService captured from the root DI container." Under real concurrency, two exchanges drove that one connection and the driver threw errors like "A command is already in progress."
Now each exchange gets its "own DI scope โ its own scoped IRedbService โ its own pooled connection," cached and disposed with the exchange. Also added controller.Redb() for controllers.
Additional fixes: ThreadsProcessor/SedaProducer/VmProducer leaked clones on failed hand-off; WireTapProcessor leaked tap clone when callback threw; scheduled llm:// and exec:// consumers never disposed per-tick exchange; lazy producer start-up made thread-safe.
Author states: 3.3.0 is "the release after which you can actually push redb.Route hard and stop chasing flaky connection errors."
Ingest route:
From("file://docs?include=*.md")
.To("knowledge://handbook?chunkChars=1000&overlap=100&embed=true");
Question โ answer route:
From(Tg.Receive(token))
.Threads(4)
.Knowledge("handbook", k: 5)
.To("llm://claude")
.EndThreads()
.To(Tg.Send(token).ChatId(Header(TelegramHeaders.ChatId)));
Tool-based alternative (model decides when to search):
context.AddRoutes(new KnowledgeSearchTool(new KnowledgeSearchOptions {
Collection = "handbook",
EmbeddingProvider = embeddings
}));
From(Tg.Receive(token))
.Threads(4)
.To("llm://claude?tools=knowledge_search")
.EndThreads()
.To(Tg.Send(token).ChatId(Header(TelegramHeaders.ChatId)));
Fail-fast guard on provider connection โ If same IRedbService entered from two threads, throws a clear InvalidOperationException naming the cause. Uses cheap Interlocked check.
Query parser fix for array.Contains(x) in WhereRedb on .NET 9 / C# 13 โ string[] .Contains(x) inside a predicate was binding to ReadOnlySpan overload (MemoryExtensions.Contains) instead of Enumerable.Contains, causing NotSupportedException. Parser now recognizes and translates to IN clause.
ComputeHash() NRE fix โ On object with Props == null, the path dereferenced before null check. Now returns null (โ Guid.Empty) consistently.
Connection-pool leak fix โ A throw from driver's transaction DisposeAsync() skipped disposing _connection, preventing return to pool. Under failure bursts the leak drained the pool. Now uses try/finally on both connection and transaction wrapper.
redb.Route.Sqs and redb.Route.Telegram ship in the distribution.Quartz config section โ Tsak now always hands out one shared IScheduler (falling back to in-memory RAMJobStore). AdoJobStore still used for persistence across nodes.IRedbService โ UsersController previously used shared singleton; now per-request via controller.Redb().Shipping: NuGet packages, Docker images (redb-tsak-{worker,web,stack} on .NET 9, tags :3.3.0-net9, :3.3.0, :latest) on ghcr.io/redbase-app, cosign-signed, plus standalone archives with checksums.txt and .bundle signatures.
"No catch." Pro remains proprietary (closed source) but across the 3.x line it is "handed out for free and with no bookkeeping โ no licenses, keys, sign-up, license server, or node/volume limits."
What was previously paid, now free: - redb core: change tracking, bulk ops, advanced cache, analytical queries (grouping, window functions) - Tsak: cluster with coordinator, distributed lock, leader election, failover
"dotnet add package and you're working."
ConcurrentConsumers(N > 1) โ now genuinely parallelize; ensure thread-safe handlers. If order matters, keep at 1.IRedbService in routes โ no action needed; per-exchange connections auto-enable. Old connection-in-progress errors should disappear.3.3.0; older 3.2.x connectors are API-compatible but lack concurrency fixes.# NuGet packages (core and providers)
dotnet add package redb.Core
dotnet add package redb.Postgres # or redb.MSSql / redb.SQLite
dotnet add package redb.Postgres.Pro # Pro โ free, no key
# Engine and connectors
dotnet add package redb.Route
dotnet add package redb.Route.Sqs
dotnet add package redb.Route.Telegram
dotnet add package redb.Route.Llm
# Runtime (Docker)
docker pull ghcr.io/redbase-app/redb-tsak-stack:3.3.0
# Verify signature
cosign verify --key cosign.pub ghcr.io/redbase-app/redb-tsak-worker:3.3.0
ConcurrentConsumers(N) fix is the headline maturity improvement โ it was "quietly broken under load"