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


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 (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


The Three Layers

  1. 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).

  2. 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.

  3. 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.

Basic redb Usage

[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 โ€” What's New in 3.3.0

Two New Transports

Amazon SQS/SNS (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.

Telegram (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).


RAG, End to End: 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 Source

A 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.


The Big Fix: 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:

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.


Per-Exchange Connections โ€” End of the Captive Singleton

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."


Putting It Together: RAG Bot on Telegram

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)));

redb (the DB Core) โ€” What Made It In

  1. Fail-fast guard on provider connection โ€” If same IRedbService entered from two threads, throws a clear InvalidOperationException naming the cause. Uses cheap Interlocked check.

  2. 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.

  3. ComputeHash() NRE fix โ€” On object with Props == null, the path dereferenced before null check. Now returns null (โ†’ Guid.Empty) consistently.

  4. 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.Tsak (the Runtime) โ€” What Made It In

  1. New connectors bundled โ€” redb.Route.Sqs and redb.Route.Telegram ship in the distribution.
  2. Dashboard scheduler shows cron jobs without Quartz config section โ€” Tsak now always hands out one shared IScheduler (falling back to in-memory RAMJobStore). AdoJobStore still used for persistence across nodes.
  3. Users admin API uses per-request scoped IRedbService โ€” UsersController previously used shared singleton; now per-request via controller.Redb().
  4. From 3.2.1: Endpoints page no longer hides anonymous contexts it counted in stats; standalone web archive starts on port 8080 instead of 5000.

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.


About Pro Being Free

"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."


Upgrading from 3.2.x โ€” Checklist

  1. Routes with ConcurrentConsumers(N > 1) โ€” now genuinely parallelize; ensure thread-safe handlers. If order matters, keep at 1.
  2. Shared state in handlers โ€” anything that worked because route was effectively single-threaded can now run from N threads.
  3. IRedbService in routes โ€” no action needed; per-exchange connections auto-enable. Old connection-in-progress errors should disappear.
  4. Version alignment โ€” everything at 3.3.0; older 3.2.x connectors are API-compatible but lack concurrency fixes.
  5. Tsak: rebuild or pull fresh image โ€” new connectors only land in shared layer after rebuild.

Getting It

# 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

Series (earlier posts)

  1. Leaving MassTransit for a Camel state of mind: the Kafka connector, Scatter-Gather, and transactions
  2. Apache Camel for .NET, dissected: the HTTP connector with no ASP.NET MVC + the Content-Based Router
  3. redb.Route โ€” Apache Camel for .NET: 22 transports, 30+ EIP patterns, compiled DSL

Key Claims