Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
154874 stories
·
33 followers

#482 Mr. Beast's episode

1 Share
Topics covered in this episode:
Watch on YouTube

About the show

Brian #1: CVE-2026-48710: A Maintainer's Perspective

  • Marcelo Trylesinski
  • suggested by Lee Luocks
  • Short version:
    • users of Starlette: upgrade to Starlette 1.0.1
    • security professionals: we can’t treat open source projects like corporations
  • This top link is a Starlette security advisory with the title
    • Missing Host header validation poisons request.url.path, bypassing path-based security checks
  • The CVE apparently caused some negative press targeting starlette.
  • However, “the vulnerability came from the application pattern and the deployment, never from something Starlette intended.”
  • A quote from an OSTIF article: “This bug is a classic “responsibility gap” where if this maintainer didn’t patch, thousands of exposed projects would have to individually secure their projects. In doing this work, they’ve voluntarily taken on the responsibility to protect the ecosystem from long-term systemic harm. As with all open source projects, they owed us nothing and could have left this to be everyone else’s problem and took the extraordinary steps of helping the ecosystem.”
  • Both X40 D-Sec and Ars Technica expected immediate fixes and responses from Starlette.
  • That’s not good. We can do better.

Michael #2: daily-stars-explorer

  • Explore the full history of any GitHub repository.
  • 📈 Full Star History - Complete daily star counts for any repo
  • ⏰ Hourly Stars - Hour-by-hour activity with timezone support
  • 🔀 Compare Repos - Side-by-side comparison of any two repositories
  • 📊 Activity Timelines - Commits, PRs, Issues, Forks, Contributors over time
  • 📌 Pin Favorites - Bookmark repos for quick access without retyping
  • 📰 Feed Mentions - See when repos were mentioned on HN, Reddit, YouTube, GitHub
  • 💾 Export Data - Download as CSV or JSON
  • 🌙 Dark Mode - Easy on the eyes
  • Try/use it online at emanuelef.github.io/daily-stars-explorer or install it for yourself.

Brian #3: Markdown to pdf with pandoc and typst

  • typst suggestion from Matt Harrison
  • Markdown is awesome
  • Pandoc is great for converting markdown to tons of stuff
    • but for pdf, it goes through LaTeX, which is … yuk (my opinion)
  • Pandoc also can convert to typst
  • And typst creates beautiful pdfs and is way easier (my opinion) to deal with than LaTeX.
  • New tools
    • brew upgrade pandoc
    • brew install typst
  • Now convert
    • pandoc something.md --to typst -o something.typ
    • typst compile something.typ something.pdf

Michael #4: postman2pytest

  • via Mikhail
  • Based on postman app
  • Convert Postman Collection v2.1 JSON into executable pytest test suites
  • Postman collections document your API. postman2pytest turns that documentation into executable regression tests that run in CI. No manual rewriting, no drift.

Extras:

Joke: Centering a div





Download audio: https://pythonbytes.fm/episodes/download/482/mr.-beasts-episode.mp3
Read the whole story
alvinashcraft
11 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Azure Developer Associate Renewal – Study Guide – 2026

1 Share

The Azure Developer Associate certification can be found here;

https://learn.microsoft.com/en-us/certifications/azure-developer/

Note: This exam is being retired on July 31st 2026, so renew now to make sure you don’t lose it!

I used the following links to pass the 2026 Renewal Certification;

CategorySub CategoryLinks
KeyvaultStoragehttps://learn.microsoft.com/el-gr/azure/key-vault/general/soft-delete-overview?utm_source=chatgpt.com
Storagehttps://learn.microsoft.com/en-au/azure/key-vault/general/key-vault-recovery?utm_source=chatgpt.com&tabs=azure-portal
Encryptionhttps://learn.microsoft.com/en-us/azure/security/fundamentals/encryption-customer-managed-keys-support
App ConfigKeys and Labelshttps://learn.microsoft.com/en-gb/azure/azure-app-configuration/concept-key-value#label
Feature Managementhttps://learn.microsoft.com/en-us/azure/azure-app-configuration/feature-management-dotnet-reference
Feature Managementhttps://learn.microsoft.com/en-us/azure/azure-app-configuration/manage-feature-flags?tabs=switch
Feature Managementhttps://learn.microsoft.com/azure/azure-app-configuration/concept-feature-management
Encryptionhttps://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-customer-managed-keys?tabs=azurerbac
Application InsightsDashboards and Alertshttps://learn.microsoft.com/en-us/azure/azure-monitor/app/metrics-overview?tabs=standard
OpenTelemetryhttps://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-add-modify?tabs=aspnetcore
OpenTelemetryhttps://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-11?view=aspnetcore-10.0&tabs=minimal-apis
OpenTelemetryhttps://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-span
Performancehttps://learn.microsoft.com/en-us/azure/azure-monitor/app/javascript-sdk?tabs=javascriptwebsdkloaderscript
Performancehttps://learn.microsoft.com/en-us/azure/azure-monitor/app/codeless-overview
Availabilityhttps://learn.microsoft.com/en-us/azure/azure-monitor/app/availability
Container AppsSecretshttps://learn.microsoft.com/en-us/azure/container-apps/manage-secrets?tabs=azure-portal
Deploymenthttps://learn.microsoft.com/en-us/azure/container-apps/get-started?tabs=bash
Deploymenthttps://learn.microsoft.com/en-us/azure/container-apps/azure-resource-manager-api-spec?tabs=arm-template
Deploymenthttps://learn.microsoft.com/en-gb/azure/container-apps/logging
Scalinghttps://learn.microsoft.com/en-us/azure/container-apps/scale-app?pivots=azure-cli
Container RegistryPublishinghttps://learn.microsoft.com/en-us/azure/devops/pipelines/ecosystems/containers/publish-to-acr?view=azure-devops&tabs=javascript%2Cportal%2Cmsi
Publishinghttps://learn.microsoft.com/en-us/azure/container-registry/container-registry-get-started-azure-cli
Authoringhttps://docs.docker.com/reference/dockerfile/#from
SMBhttps://learn.microsoft.com/en-us/azure/container-instances/container-instances-volume-azure-files
SMBhttps://learn.microsoft.com/en-us/azure/storage/files/storage-files-introduction
Azure FunctionsTriggershttps://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library?tabs=v4%2Ccmd
Triggershttps://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings?tabs=isolated-process%2Cnode-v4%2Cpython-v2&pivots=programming-language-csharp
Triggershttps://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-python?pivots=python-mode-decorators
Triggershttps://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-python?pivots=python-mode-decorators#programming-model
Configurationhttps://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=azure-portal%2Cto-premium
StorageTagginghttps://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobcontainerclient.getpropertiesasync?view=azure-dotnet
Tagginghttps://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobcontainerclient.setmetadataasync?view=azure-dotnet
Soft Deletehttps://learn.microsoft.com/en-us/azure/storage/blobs/soft-delete-container-overview
Encodinghttps://learn.microsoft.com/en-us/rest/api/storageservices/Naming-and-Referencing-Containers–Blobs–and-Metadata
Encodinghttps://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-properties-metadata
Cosmos DBPartitioninghttps://learn.microsoft.com/en-us/azure/cosmos-db/partitioning
Containershttps://learn.microsoft.com/en-us/azure/cosmos-db/how-to-dotnet-get-started
Change Feedhttps://learn.microsoft.com/en-us/azure/cosmos-db/change-feed-processor?tabs=dotnet
Change Feedhttps://learn.microsoft.com/en-us/azure/cosmos-db/change-feed
Consistencyhttps://learn.microsoft.com/en-us/azure/cosmos-db/consistency-levels
Consistencyhttps://learn.microsoft.com/en-us/azure/cosmos-db/how-to-manage-consistency?tabs=portal%2Cdotnetv2%2Capi-async
Triggershttps://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-trigger?tabs=python-v2%2Cisolated-process%2Cextensionv4%2Cnodejs-v4%2Cidentity-based&pivots=programming-language-csharp

The post Azure Developer Associate Renewal – Study Guide – 2026 first appeared on Pete Codes.

The post Azure Developer Associate Renewal – Study Guide – 2026 appeared first on Pete Codes.

Read the whole story
alvinashcraft
29 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Enterprise Integration Patterns in .NET, the deep-dive series — Part 1: the four in-memory channels (and the Exchange they carry)

1 Share

The earlier posts were the tour: what redb.Route is, an Apache Camel-style ESB for .NET — a fluent C# integration DSL with 22 connector projects (~30 URI schemes once you count the https/wss/es variants), ~30 Enterprise Integration Patterns implemented natively across 41 processors, 8 in-process components, a compiled expression engine, and a pluggable marshal/unmarshal layer. Enough touring.

This starts a deep-dive series — one piece at a time, the actual DSL, the actual semantics, the gotchas that bite you the first time. Not a feature list. A working manual. The series runs on four parallel tracks; you can follow whichever maps to the problem in front of you.

Track A — the in-process foundation (8 components)

The wires and the message model everything else stands on. Small surface, deep behavior.

  1. The four channels + the Exchangedirect, direct-vm, seda, vm, and the object they carry. (this post — the foundation)
  2. Scheduling & test componentstimer, plus quartz/cron built on the same idea, and the log / mock / validator components you'll actually lean on in tests.

Track B — the Enterprise Integration Patterns (~30, across 41 processors)

Grouped the way Hohpe & Woolf's Enterprise Integration Patterns groups them, one article per cluster, each dissected against the shipped processor. (The codebase has 41 processors total; a handful — To, Log, Delegate, RoutePolicy — are plumbing rather than named patterns, so the honest count of distinct EIP is around thirty plus the 6 load-balancer strategies.)

  • Message RoutingChoice (Content-Based Router), Filter, Splitter (+ streaming splitter), Aggregator, Resequencer, Multicast, RecipientList, ScatterGather, DynamicRouter, LoadBalance (Round-Robin / Random / Weighted / Sticky / Failover).
  • Message TransformationMarshal/Unmarshal and the data-format registry, ConvertBody, Enrich/PollEnrich (Content Enricher), ClaimCheck, StreamCaching, Normalize, and Transform/SetBody/SetHeader over the expression engine.
  • Messaging EndpointsIdempotentConsumer, WireTap, competing consumers (seda/vm concurrentConsumers), Pipeline, request/reply (InOut), Bean<T>.
  • System Management & ReliabilityOnException / TryCatch / DeadLetterChannel, CircuitBreaker, Throttle / KeyedThrottle, Delay / Debounce / Sampling, Timeout, Loop, Saga (+ compensation), RoutePolicy, Transaction/Transacted, and Validate (JSON Schema / XSD).

Track C — the expression & predicate engine (its own article, and it earns it)

The one piece every other track quietly depends on. I almost wrote it off as string interpolation in the roadmap — then I read the source. It's a genuine compiled little language: TokenizerParser → an AST (BinaryOperationNode, UnaryOperationNode, FunctionCallNode, TernaryNode, PostfixOperationNode, property/index access), lowered to System.Linq.Expressions and .Compile()d to real IL — not tree-walked at runtime. There's a dedicated article because there's a dedicated language:

  • The ${...} Simple-style template isn't formatting — it's the entry point to the whole grammar: arithmetic (+ - * /), comparison, AND/OR/XOR/NOT, the ternary ?:, even postfix ++/--.
  • ~22 built-in functions, mined from the AST's FunctionCallNode: string (concat, upper, lower, trim, substring, replace, length, contains, startswith, endswith), numeric (abs, round, min, max), aggregates (sum, avg, count), date math (now, dateformat, dateadd), and the two that bridge into the structured world: jpath(...) and xpath(...).
  • JSONPath and XPath as first-class expression types — typed (TypedJsonPathExpression, TypedXPathExpression) and precompiled (CompiledJPathExpression, CompiledXPathExpression) when the path itself is static.
  • The predicate builders — the fluent half, for .Filter/.Choice/.Validate: isEqualTo, isNotEqualTo, isGreaterThan/isLessThan (…OrEqualTo), isBetween, contains, startsWith, endsWith, regex, In(...), isNull/isNotNull, composed with and/or/not. Seventeen of them, each a real IPredicate, not a closure.
  • Four separate ConcurrentDictionary compile-and-cache pools (template / property-resolver / logical / value) so every one of the above compiles once per distinct expression string and runs as a cached delegate forever after.

That's why it's its own installment, and honestly why it might be two: the template grammar and the predicate DSL are different front-ends onto the same compiler.

Track D — the 22 connectors, one article each

One focused article per connector, every example lifted from real production routes, not toy snippets:

  • Brokers & messagingkafka, rabbitmq, amqp, asb (Azure Service Bus), wmq (IBM MQ), mqtt.
  • RPC & webhttp/https, grpc, signalr, ws/wss, tcp.
  • Data & storagesql, redis, elasticsearch, s3.
  • Files & transferfile, ftp, sftp.
  • Enterprise & miscsmtp/pop3/imap (mail), ldap, fcm/fstore/fbstorage (Firebase), qtimer/cron (Quartz).

We start with Track A on purpose. Every pattern in Track B, every expression in Track C, and every connector in Track D is built on two things: a channel that carries a message from one route segment to the next, and the Exchange that is the message. Get these two right and the rest of the series is just composition. Get them wrong and you'll spend an afternoon wondering why your transaction silently didn't roll back.

A pipeline in redb.Route is From → [processors] → To. Let's look at what actually flows through it.

The Exchange — the heart, and it isn't simple

Everything that moves through a route is an IExchange. Not a byte[], not your DTO — an Exchange, a small object with more going on inside it than the name suggests. Here's the shape that matters:

public interface IExchange : IAsyncDisposable
{
    IMessage  In  { get; set; }     // the primary message — always present
    IMessage? Out { get; set; }     // the reply — lazy, null until you need it
    ExchangePattern Pattern { get; set; }   // InOnly (default), InOut, or OutOnly

    IDictionary<string, object?> Properties { get; }   // route-level metadata
    Exception? Exception { get; set; }                 // error state, in-band
    bool ExceptionHandled { get; set; }
    string ExchangeId { get; }                         // identity, stable across clones

    IExchange Clone();                                 // deep-ish copy + NEW DI scope
    IServiceProvider? ServiceProvider { get; }         // per-exchange DI scope
}

And the message it carries:

public interface IMessage
{
    object? Body { get; set; }                        // your payload — any object, or null
    string? ContentType { get; set; }
    IDictionary<string, object?> Headers { get; }     // metadata that DOES travel to brokers
    T? GetHeader<T>(string key);
    IMessage Clone();
}

Five things about this object decide how every channel and every pattern behaves. None of them are obvious from the type signature.

1. In vs Out, and the Pattern

In is the message coming through. Out is the reply, and it's lazy — for the default InOnly pattern it stays null and is never allocated. It only appears when a processor explicitly sets it (request/reply, or .Respond()).

There are three patterns, not two — this is the Apache Camel 2.x model, ported wholesale:

public enum ExchangePattern
{
    InOnly  = 0,   // fire-and-forget. Producer result is written to In; Out stays null. (default)
    InOut   = 1,   // request/reply. Original preserved in In, response written to Out.
    OutOnly = 2,   // explicit response via .Respond(); the RPC reply is taken from Out.
}

Here's the part the type signature hides, and the one place people get it wrong: HasOut does not tell you where the answer is. Even on an InOut exchange, a processor isn't obligated to populate Out — if it just mutates In.Body, the result lives in In. So the framework never trusts HasOut to find a reply. It reads Out ?? In, every time:

// ProducerTemplate.RequestBody — the canonical reply-extraction rule
exchange.Pattern = ExchangePattern.InOut;
await producer.Process(exchange);
return exchange.Out?.Body ?? exchange.In.Body;   // Out if present, otherwise In

This is verbatim how Camel's ProducerTemplate extracts a result (getResultMessage: "has Out → use Out, else In"). Copy that rule into your own code — exchange.Out ?? exchange.In — and you'll never chase a reply that quietly stayed in In. HasOut is a fact about allocation, not about where the data is; don't use it to route on the answer.

One honesty note for the JVM crowd: the live Out message and the OutOnly pattern are Camel 2.x semantics. Camel 3+ deprecated getOut()/setOut() and collapsed the pattern set toward InOnly/InOut, precisely because a separate Out message copied headers and bred subtle bugs. redb.Route keeps the fuller 2.x model on purpose — but if you're coming from modern Camel, that's the difference you'll notice first.

2. Properties vs Headers — the distinction that leaks bugs

Both are IDictionary<string, object?>. They are not interchangeable:

  • In.Headers travel with the message to the broker. Put a correlationId here and Kafka/RabbitMQ carry it downstream.
  • exchange.Properties are route-level metadataRouteId, transaction markers, your own scratch state. They do not leave the process (the interface XML doc says exactly that: "Does NOT travel to brokers — use In.Headers for that"). Stash a DbContext handle or a retry counter here.

Put a value in the wrong dictionary and it either fails to reach the consumer (you used Properties) or leaks internal state onto the wire (you used Headers). The compiler won't catch it; both are just string-keyed dictionaries. Knowing which is which is half of using the framework correctly.

Read either with the typed accessors instead of casting by hand:

var attempt = exchange.GetProperty<int>("retryCount");     // route-level, stays in-process
var corr    = exchange.In.GetHeader<string>("correlationId"); // travels to the broker

The well-known keys the framework itself writes

This is the part that makes the section concrete, and it's the answer to "what's actually in Properties?" The pipeline and the processors populate a set of reserved keys as your exchange flows. There is no single ExchangeProperties constants file — they live next to the processor that owns each one — but here is the real registry, mined from source.

exchange.Properties — route-level, never leave the process:

Key Constant in code Written by Meaning
TRANSACT_ACTION TransactedProcessor.TransactActionPropertyKey .Transacted() the transacted-action stack for synchronization
TRANSACTION_SCOPE BeginTransactionProcessor.ScopePropertyKey (internal) .Transaction() the live TransactionScope for the block
CamelDuplicateMessage IdempotentConsumerProcessor.DuplicatePropertyKey Idempotent Consumer true when the message was seen before
ClaimCheck.Stack ClaimCheckHeaders.StackPropertyKey (internal) Claim Check the stack of stored payload keys
ValidationErrors / ValidationResult ValidateProcessor.*Property .Validate() validation outcome for the current exchange
CamelSplitSize — (streaming splitter) streaming Splitter running count of split parts
__redb_scope:* — (prefix) DI plumbing named child DI scopes, freed by ReleaseScopes()

Plus RouteId is promoted to a first-class property on the exchange (exchange.RouteId), not just a dictionary entry — that's what the logger prints as [rId:…].

In.Headers — travel with the message:

The Camel-compatible message headers come from the same world Camel users expect. The Splitter, for example, stamps every part:

// SplitterProcessor — each split message carries its coordinates
splitMessage.Headers["CamelSplitIndex"]    = index;          // 0-based position
splitMessage.Headers["CamelSplitSize"]     = total;          // batch size
splitMessage.Headers["CamelSplitComplete"] = index == total - 1; // last one?

and every transport contributes its own namespaced header constants — KafkaHeaders (redbKafka.Topic, redbKafka.Partition, redbKafka.Offset, …), SqlHeaders (redbSql.rowCount, redbSql.generatedKeys, …), SignalRHeaders (redbSignalR.ConnectionId, …), TcpHeaders, WsHeaders, ElasticsearchHeaders. Each is a static class of public const string so you bind against KafkaHeaders.Offset, not a stringly-typed "redbKafka.Offset" you might misspell. The rule of thumb: anything prefixed Camel* or redb<Transport>.* is a header (on the wire); anything in Properties is yours and the process's alone.

3. The exception travels in-band

When a processor throws, the exception doesn't just unwind the stack — it's captured onto exchange.Exception, with an ExceptionHandled flag beside it. That's what makes a dead-letter route able to branch on why something failed (when e.Exception is TimeoutException → …) instead of just that it failed. The error becomes data you can route on. We lean on this hard in the error-handling installment.

4. ExchangeId is stable across clones

Each exchange gets a Guid-based ExchangeId at creation. The non-obvious part: Clone() preserves it. A split into 500 parts, or a seda hop that clones the exchange, keeps the same id — so your logs and traces stitch the whole flow back to one origin. Identity survives copying; that's deliberate.

5. The DI scope — and four ways to copy an exchange

This is the part that's genuinely not simple, and it's the reason the channels behave the way they do.

An Exchange can own a DI scope — a per-message IServiceScope. Processors resolve scoped services (DbContext, IRedbService, …) from exchange.ServiceProvider, and they get the same instances for the lifetime of that exchange. A TransactionScope lives in exactly that scope. So the question "are these two exchanges in the same transaction?" reduces to "do they share a DI scope?"

There are four ways an exchange gets copied, and they differ only in what they do with that scope:

Method Body/Headers DI scope Owns scope? Use
Clone() copied new scope yes hand-off to another thread (seda, vm)
CloneLinked() copied shares parent's no parallel fan-out inside the parent's transaction
CreateChild(msg) new message new scope yes a derived exchange, independent lifetime
CreateLinkedChild(msg) new message shares parent's no sequential children reusing the same connection
// from Exchange.Clone() — the scope-creating branch
if (_scopeFactory != null)
{
    clone._scopeFactory = _scopeFactory;
    clone._scope = _scopeFactory.CreateScope();   // <-- a brand-new scope
}
// from Exchange.CloneLinked() — the scope-sharing branch
_ownsScope = false,
_scope = _scope,            // <-- the SAME scope, and we won't dispose it
_scopeFactory = _scopeFactory

Hold onto that table. The entire transaction story of seda vs direct, and of Multicast vs a broker hop, is just which row got used. Clone() (new scope) means a new transaction; CloneLinked() (shared scope) means the same one.

There's also ReleaseScopes() — it disposes the DI scopes without touching the Body, so an aggregator can free database connections early while still holding the message data it's accumulating. And DisposeAsync() cleans up both the body (streams, stream caches) and the scopes. The object is IAsyncDisposable for a reason.

The gotcha: Clone() does NOT deep-copy the Body

Read Message.Clone() literally:

public IMessage Clone()
{
    var clone = new Message(Body) { ContentType = ContentType };  // Body reference copied
    foreach (var kvp in _headers)
        clone._headers[kvp.Key] = kvp.Value;
    return clone;
}

Headers get a fresh dictionary. Body is copied by reference. After a seda hop the producer's exchange and the worker's clone have independent headers, properties, and DI scopes — but they point at the same body object. If that body is a mutable List<T> or a POCO and both sides write to it, you have a data race the cloning looks like it prevented. The XML doc says "deep copy"; the honest truth is "deep copy of everything except the payload." Treat the body as immutable once it's in flight, or clone it yourself.

Two APIs on purpose

One last thing you'll notice in IntelliSense: every member has a C# idiomatic form (In, Body, GetHeader<T>) and a Java-style alias (getIn(), setBody(), getHeader<T>()). They're default interface methods over the same state, kept so the model reads the same as Apache Camel for anyone coming from the JVM. Use whichever; they're the same object.

Now — the wires that carry this thing.

The four channels — two axes

direct, direct-vm, seda, vm are how route segments talk to each other inside a process. Choosing between them is the single most common thing newcomers get wrong, and now you have the vocabulary for why: it comes down to threading and scope. They split on two axes — sync vs async, and one context vs across contexts:

Scheme Sync/Async Scope Clones the exchange? Same transaction?
direct:// synchronous one context no yes — same thread, same scope
direct-vm:// synchronous across contexts no yes
seda:// asynchronous one context yes (Clone()) no — new scope
vm:// asynchronous across contexts yes no

direct is the contrast. seda is where the real work — and the real footguns — live, so that's where we'll spend the page.

direct:// — a method call wearing a URI

direct is not a queue. No thread, no buffer. A producer sending to a direct endpoint invokes the consumer's processor synchronously, on the same thread:

// the whole of DirectProducer.Process
var processor = _endpoint.ConsumerProcessor
    ?? throw new InvalidOperationException("No consumer registered for direct endpoint ...");
await processor.Process(exchange, ct);

The exchange is not cloned. Same object, same thread, same DI scope — straight to the consumer. Three consequences follow:

  • Exceptions propagate back to the caller. A throw in the direct consumer surfaces in the producer's route, where OnException/DoTry can catch it. (Remember §3 — it also lands on exchange.Exception.)
  • It's the same transaction. Same scope from the table above, so a direct hop inside a .Transaction() block commits and rolls back with everything around it.
  • The consumer must be started first, or the send throws. direct decouples your route definitions into named sub-routes — not your threads.

That's the entire personality of direct: a zero-cost, in-transaction call you can give a URI and reuse. It has no parameters because it has no machinery. Use it to break a big route into readable, reusable pieces.

seda:// — the async queue, in detail

seda (Staged Event-Driven Architecture) is the opposite of direct in every cell of the table. It's a real in-memory queue built on System.Threading.Channels. The producer enqueues and returns immediately; one or more background workers drain the queue on their own threads.

// SedaProducer.Process — the whole thing
var copy = exchange.Clone();                       // §5: new scope. the trap lives here.
await _endpoint.Queue.Writer.WriteAsync(copy, ct);

Two facts are baked into those two lines, and everything else about seda follows from them: it clones (so the worker and producer never share scope — that Clone() is row 1 of the table, a new scope), and it returns before the work is done (so the producer's thread, and its transaction, move on).

The parameters

seda takes three, all on the URI: seda://name?concurrentConsumers=4&size=1000&timeout=30000.

Parameter Default What it does When to change it
concurrentConsumers 1 Number of worker loops draining the queue in parallel Raise when the downstream is slower than the inflow and order doesn't matter
size 0 (unbounded) Max queued exchanges. 0 = grow without limit; >0 = bounded with Wait backpressure Set a bound whenever the producer can outpace the consumer (almost always, in production)
timeout 30000 Declared as the enqueue wait for a bounded queue — see the honesty note below

concurrentConsumers — throughput, at the cost of order

One worker is the default and keeps strict FIFO. Raising it spins up N independent loops:

// SedaConsumer.RunAsync
_workers = new Task[_options.ConcurrentConsumers];
for (var i = 0; i < _options.ConcurrentConsumers; i++)
    _workers[i] = WorkerLoop(pollCt, processingCt);
// each worker
await foreach (var exchange in _endpoint.Queue.Reader.ReadAllAsync(pollCt))
{
    await ProcessWithTracking(exchange, processingCt);
    Interlocked.Increment(ref _processedCount);
}

Two consequences worth stating plainly:

  • You trade ordering for throughput. With concurrentConsumers=1 the channel runs in SingleReader mode (a real optimization in System.Threading.Channels) and messages come out in order. With N>1, N workers pull concurrently and strict FIFO is gone — message 2 can finish before message 1. Only raise it when out-of-order processing is acceptable.
  • It's per-endpoint backpressure relief: a slow downstream stops blocking the upstream producer, because the producer only ever writes to the queue and leaves.

size — bounded vs unbounded, and why you almost always want bounded

This is the parameter people skip and regret. The endpoint picks the channel implementation off size:

Queue = options.Size > 0
    ? Channel.CreateBounded<IExchange>(new BoundedChannelOptions(options.Size)
      {
          FullMode = BoundedChannelFullMode.Wait,    // producer awaits a free slot
          SingleReader = options.ConcurrentConsumers == 1,
          SingleWriter = false
      })
    : Channel.CreateUnbounded<IExchange>(/* ... */); // grows until you run out of memory
  • size=0 (default, unbounded): the queue grows as fast as producers write. If the consumer can't keep up, that's an unbounded memory leak with extra steps. Fine for bursty, bounded-volume work; dangerous for a firehose.
  • size>0 (bounded): FullMode = Wait means a full queue makes the producer await a free slot — backpressure that pushes the slowdown upstream instead of into your heap. This is what you want in production.
// bounded SEDA: 4 workers, at most 1000 queued, producer waits when full
From("seda://enrich?concurrentConsumers=4&size=1000")
    .Process(async (ex, ct) => await Enrich(ex, ct))
    .To("rabbitmq://enriched");

timeout — an honesty note

The options object documents timeout (default 30000 ms) as the enqueue wait for a bounded queue. Being straight with you: in the current code the producer enqueues with WriteAsync(copy, ct) and does not apply that timeout — a full bounded queue makes the producer wait on the channel until a slot frees or the CancellationToken fires, not until 30 seconds elapse. So today, plan around size and the cancellation token; treat timeout as declared-but-not-yet-wired and don't build a deadline assumption on it. (Flagging it here because guessing at framework behavior from a doc-comment is exactly how you ship a bug.)

Graceful shutdown — seda drains, it doesn't drop

When a route stops, seda doesn't throw away what's already queued:

// SedaConsumer.OnStopAccepting
_endpoint.Queue.Writer.TryComplete();   // stop accepting new; let readers finish

Completing the writer makes the workers' ReadAllAsync loop finish the remaining items and then exit cleanly (SedaConsumer is a DrainableConsumer). On a graceful stop, in-flight queued exchanges are processed, not lost.

The durability caveat

seda is in-memory and non-durable. A graceful stop drains; a crash or a hard kill does not — whatever was sitting in the channel is gone. seda is at-most-once across a process restart. When you need the queue to survive a restart, that's a broker (rabbitmq, kafka), not seda. seda is for decoupling within a process, not for durability.

The transaction trap, stated once and for all

Now §5 pays off. Because seda calls Clone() — row 1, a new DI scope on a different thread — anything past a seda:// hop is not in the caller's transaction.

From("kafka://orders")
    .Transaction()
        .To(Sql.Execute("INSERT …").Transacted())
        .To("seda://post-process")     // runs in a NEW scope, on another thread,
    .EndTransaction();                 //    OUTSIDE this transaction

If post-process throws, the INSERT above has already committed — the seda hop left the transaction the moment it cloned. This is the one mistake everyone makes exactly once. The fix is the table: if the hop must share the transaction, use direct (no clone, same scope); if you genuinely want to hand the work off and move on, seda is correct and you accept the new boundary. The DSL is the same; the scope is everything.

Mental model: direct = function call, seda = mailbox. One preserves your thread and your transaction; the other trades both for throughput and isolation.

direct-vm:// and vm:// — the same two, across module boundaries

In a multi-module host (this is how redb.Tsak runs several modules in one process) each module is its own RouteContext. Plain direct and seda are scoped to a single context — a producer in module A can't see a direct consumer in module B. The -vm variants lift exactly that wall by sharing the processor registry, and for vm the channel, through a SharedVmRegistry DI singleton:

  • direct-vm:// — synchronous, cross-context, no clone. A consumer in the billing module exposes direct-vm://charge; a producer in orders calls it like a local in-transaction method.
  • vm:// — asynchronous, cross-context, cloned-and-queued. The cross-module twin of seda, with the same concurrentConsumers and size parameters (and the same Clone(), so the same transaction boundary and the same shared-Body caveat).

The rule transfers cleanly: direct-vm for a synchronous cross-module call that shares the caller's transaction; vm for hand-it-off-and-move-on across modules. Same semantics as their in-context twins — just a wider blast radius.

Picking a channel

The whole decision in one table:

You want… Channel
A reusable sub-route, same thread, inside my transaction direct://
The above, but the consumer lives in another module direct-vm://
To hand work off to a background worker and not wait seda://
The above, across module boundaries vm://
Survival across a process restart not a channel — a broker (rabbitmq, kafka)

And the two facts that drive every row: does it clone (new scope = new transaction), and does it return before the work is done. Everything else is detail.

What's next

That's the foundation. You now know what flows through a route (Exchange — In/Out, Properties vs Headers, in-band exceptions, and the four scope-aware clone variants) and the four wires that carry it (direct/direct-vm sync-and-in-transaction, seda/vm async-and-isolated), down to the parameter that decides whether your queue applies backpressure or eats your heap.

Next in the series — Part 2: Splitter + Aggregator. We fan one message into many, process them with bounded parallelism, and re-assemble — and the Clone() vs CloneLinked() distinction from §5 turns out to be the whole story of whether the split shares the parent's transaction. Plus the aggregation-strategy contract where the first call hands you a null accumulator. Subscribe to the series if you want it when it lands.

If anything here fought you — especially the seda transaction boundary or the shared-Body clone — say so in the comments. That feedback is exactly what an early OSS release is for.

Links

All Apache 2.0. Questions in the comments are welcome — especially "does channel X do Y?", because that's how the docs get written.

Read the whole story
alvinashcraft
43 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

AdaBoost.R2 Regression Using C#

1 Share
AdaBoost.R2 regression works by building an ensemble of decision trees, training them on reweighted data, and combining their predictions with a weighted median, while also showing how parameter choices affect accuracy and overfitting.
Read the whole story
alvinashcraft
59 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

The placeholder name for the Windows 8 experience was “modern”

1 Share

During the development of Windows 8, we needed a name for “that thing we’re creating.” Not being a particularly clever bunch when it comes to code names, we just called it “the modern experience,” to distinguish it from what we had in Windows 7, which was called “the classic experience.”

And then, as Microspeak demands, we started abbreviating like mad.

The new shell was called the “modern shell” or “MoSh” for short. By comparison, the old shell was called the “classic shell”, which some people started calling “ClaSh” for short. (That name didn’t stick.)

When we couldn’t come up with a name for a component of the modern experience, a common fallback was to stick the prefix “Mo” in front.

The new Start menu derived from some earlier explorations known as the “Go page” (since it’s the place you go when you want to do something). Its new code name was therefore “MoGo.”

The portion of the screen for snapped applications was called the “MoBar”, and the portion of the screen used for filled applications was called the “MoBody.”

The settings control panel? “MoSet.”

The ListView control? It started out with the more tedious name “modern collection control”, which got shortened to “MoCo.”

Even the new applications got the Mo-treatment. The new Web browser initially called itself “MoB”, but then decided that an even hipper name would be “MoBro.”

And the new photo manager? The people who worked on that didn’t want to get left out of the “Mo”-party, so they called themselves (wait for it) “MoPho.”

I hope somebody put their foot down out of frustration. “Enough already. This Mo thing is completely out of control.”

Windows 8 was announced fifteen years ago today, on June 1, 2011.

The post The placeholder name for the Windows 8 experience was “modern” appeared first on The Old New Thing.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Get started with OpenAI GPT-5.5, GPT-5.4 models, and Codex on Amazon Bedrock

1 Share

As we previewed in What’s Next with AWS 2026, we’re announcing the general availability of OpenAI GPT-5.5, GPT-5.4 models, and Codex on Amazon Bedrock, giving you access to frontier models and a coding agent for software development.

According to OpenAI, GPT-5.5 and GPT-5.4 models are excellent for coding, reasoning, agentic workflows, and complex professional work. You can use GPT-5.5 for the hardest customer workloads and GPT-5.4 for the best price-performance. You can call them through Responses API on Amazon Bedrock’s next-generation inference engine built for high performance, reliability, and security.

Codex is the OpenAI coding agent for AI-powered software development. According to OpenAI, more than 4 million developers use Codex every week to write, refactor, debug, test, and validate code across large codebases. With GPT-5.5 powering inference, Codex introduces a new class of intelligence optimized for complex, long-horizon developer workflows. You can use the Codex App, the Codex CLI, and IDE integrations with Visual Studio Code, JetBrains, and Xcode, with all model inference routed through the Responses API on Amazon Bedrock.

For customers with data residency requirements, all processing stays within the Bedrock Region you select. You pay per token with no seat licenses and no per-developer commitments.

GPT-5.5 and GPT-5.4 models on Bedrock in action
You can access the model programmatically using the OpenAI Responses API to call the bedrock-mantle endpoints through the OpenAI SDK, command-line tools such as curl.

Let’s start with OpenAI SDK for Python. Install OpenAI SDK.

pip install -U openai

Set the environment variables for authentication.

export OPENAI_BASE_URL="https://bedrock-mantle.us-east-2.api.aws/openai/v1"
export OPENAI_API_KEY="<BEDROCK_API_KEY>"
export BEDROCK_OPENAI_MODEL_ID="openai.gpt-5.5"

Here is a sample Python code to call GPT-5.5 model on Bedrock:

import os
from openai import OpenAI
 
client = OpenAI(
    base_url=os.environ["OPENAI_BASE_URL"],
    api_key=os.environ["OPENAI_API_KEY"],
)
 
response = client.responses.create(
    model=os.environ["BEDROCK_OPENAI_MODEL_ID"],
    input=[
        {
            "role": "developer",
            "content": "You are a software engineer with excellent AWS cloud knowledge. Be concise and practical.",
        },
        {
            "role": "user",
            "content": "Design a distributed architecture on AWS in Python that should support 100k requests per second across multiple geographic regions.",
        },
    ],
    reasoning={"effort": "medium"},
    text={"verbosity": "low"},
)
 
print(response.output_text)

You can call directly the model endpoint using curl.

curl "$OPENAI_BASE_URL/responses" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "openai.gpt-5.5",
    "input": [
      {
        "role": "developer",
        "content": "You are a software engineer with excellent AWS cloud knowledge."
      },
      {
        "role": "user",
        "content": "Design a distributed architecture on AWS in Python that should support 100k requests per second across multiple geographic regions."
      }
    ],
    "reasoning": {"effort": "medium"},
    "text": {"verbosity": "low"}
  }'

You can use the Responses API when you want to use model-managed multi-turn state, need hosted tools, function tools, or richer tool orchestration, and run background or long-running work. To learn more, visit the OpenAI Cookbook Responses examples and getting started guide.

Using OpenAI Codex with GPT-5.5 on Amazon Bedrock
You can download Codex CLI, Codex App or Codex VS Code extension and get started with the Bedrock for model inference. Codex supports two Bedrock authentication pathways: Amazon Bedrock API key or AWS SDK credential chain. If you set AWS_BEARER_TOKEN_BEDROCK, Codex uses it first; otherwise Codex falls back to AWS SDK credential chain.

Set AWS_BEARER_TOKEN_BEDROCK in the environment that Codex will read:

export AWS_BEARER_TOKEN_BEDROCK=<your-bedrock-api-key>

Then, configure your preferred Region and set the model ID to openai.gpt-5.5 in ~/.codex/config.toml, which is required for Bedrock API-key authentication. You can also choose openai.gpt-5.4, openai.gpt-oss-120b, or openai.gpt-oss-20b. For the desktop app or VS Code extension, put any environment variables the app needs in ~/.codex/.env.

model = "openai.gpt-5.5"
model_provider = "amazon-bedrock"
[model_providers.amazon-bedrock.aws]
region = "us-east-2"

Restart the desktop app or VS Code extension after changing ~/.codex/config.toml or ~/.codex/.env. In Codex CLI, you should see a /status tab that looks like this:

In Codex App, you can use GPT-5.5 model through Amazon Bedrock inference.

Things to know
Let me share some important technical details that I think you’ll find useful.

  • Model latency: OpenAI model information positions GPT-5.5 as fast and GPT-5.4 as medium speed, but customer-perceived latency depends on reasoning effort, output length, tool calls, background mode, Region, quotas, throttling, prompt size, and cache hits. Start GPT-5.5 at medium effort. Start GPT-5.4 with effort set explicitly rather than relying on its none default.
  • Scaling and capacity: Bedrock’s new inference engine is designed to rapidly provision and serve capacity across many different models. When accepting requests, we prioritize keeping steady state workloads running, and ramp usage and capacity rapidly in response to changes in demand. During periods of high demand, requests are queued, rather than rejected.

Now available
OpenAI GPT models and Codex on Amazon Bedrock are available today: GPT-5.5 model in the US East (Ohio) Region, GPT-5.4 model in the US East (Ohio) and US West (Oregon) Regions. Check the full list of Regions for future updates. To learn more, visit the OpenAI on Amazon Bedrock page and the Amazon Bedrock pricing page.

Give GPT-5.5, GPT-5.4 models, and Codex on Amazon Bedrock a try today and send feedback to AWS re:Post for Amazon Bedrock or through your usual AWS Support contacts.

Channy

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories