[Floci] Service Bus host ports are probed then released, so another process can take them before the Artemis sidecar binds

Aperta
#2,335 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@thomhurst ci sta già lavorando.

Dal 28/10/2026.

  • #2336 di @thomhurst — aperta

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
20/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Ferma
Stack tecnologico
csharp, docker

Direzione di ricerca

Start with FlociServiceBusEndpointAllocator and the Service Bus child resource endpoint allocation and event flow; inspect the related open pull request #2336 and the floci-az management API changes in floci-io/floci-az#592/#593. Verify that dynamically assigned host ports are discovered after floci-az starts, endpoints and connection strings become available, and explicit ports still work.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Describe the bug

When WithServiceBus() is called without explicit ports, FlociServiceBusEndpointAllocator picks the AMQP and AMQPS host ports itself during BeforeStartEvent. It binds a probe TcpListener on each candidate port, releases the probes, and passes the ports to floci-az through FLOCI_AZ_SERVICES_SERVICE_BUS_AMQP_PORT and FLOCI_AZ_SERVICES_SERVICE_BUS_AMQP_TLS_PORT. floci-az binds them much later, when it starts the Artemis sidecar with -p <port>:5672 and -p <port>:5671.

Nothing holds the port between the release and Docker's bind. That window covers the rest of AppHost startup, the floci-az image pull, and floci-az's own boot, so it is often several seconds long. Any other process can take the port in that time: another AppHost started concurrently with --isolated, a DCP proxy, a test fixture, or anything else that binds a port. Docker then fails to start the sidecar:

failed to bind host port 0.0.0.0:<port>/tcp: address already in use

floci-az logs the failed namespace start and reports /health as DOWN, so the emulator never becomes healthy and every Service Bus consumer waits forever. Docker retries a port conflict only for dynamic (0) host ports, so an explicit preselected port cannot recover.

#2240 / #2241 moved the selection to a random port in 20000-32767 so DCP's ephemeral proxy ports no longer collide with it. That removed the most common collision, but the allocator still probes and releases, so the time-of-check/time-of-use race remains for everything else that binds ports in that range, including a second AppHost making the same random choice.

Regression

Not a regression. The probe-and-release design has been in place since the allocator was added in 13.6.0.

Steps to reproduce

The race is timing-dependent, so the simplest deterministic repro takes the selected port between allocation and the sidecar's start, as another process would:

var builder = DistributedApplication.CreateBuilder(args);

var azure = builder.AddFlociAzure("floci-az").WithDockerSocket();
var serviceBus = azure.WithServiceBus();

// Simulate another process binding the port after the integration released it.
TcpListener? squatter = null;
builder.Eventing.Subscribe<BeforeResourceStartedEvent>(azure.Resource, (_, _) =>
{
    squatter ??= new TcpListener(IPAddress.Any, serviceBus.Resource.AmqpEndpoint.Port);
    squatter.Start();
    return Task.CompletedTask;
});

builder.Build().Run();
  1. Run the AppHost.
  2. floci-az logs that it could not start the default Service Bus namespace because Docker could not bind the host port.
  3. floci-az stays Unhealthy, and anything that waits for it never starts.

Without the squatter, the same failure happens intermittently when several AppHosts start at the same time on one machine (for example parallel CI jobs or test fixtures), or when another local service happens to bind the selected port first.

Expected behavior

The Service Bus sidecar starts reliably regardless of what else binds ports on the machine. Only an atomic bind can guarantee that, so the host ports should be assigned by Docker, not chosen in advance by the integration:

  1. When the caller does not pass amqpPort / amqpTlsPort, set FLOCI_AZ_SERVICES_SERVICE_BUS_AMQP_PORT=0 and FLOCI_AZ_SERVICES_SERVICE_BUS_AMQP_TLS_PORT=0. floci-az already treats 0 as a dynamic Docker binding.
  2. Leave the child's amqp / amqps endpoints unallocated until floci-az is ready, then read the ports Docker published from floci-az's management API (GET /{account}-servicebus/namespaces/default, fields amqpHostPort / amqpsHostPort), allocate the endpoints, and raise ResourceEndpointsAllocatedEvent and ConnectionStringAvailableEvent for the child.
  3. Keep explicitly configured ports exactly as they are today.

Two details matter for this:

  • The child currently implements IResourceWithParent<FlociAzureContainerResource>. Aspire publishes ConnectionStringAvailableEvent for IResourceWithParent children before it creates the parent container, and resolving the connection string waits for the child's endpoints. With ports that only exist after floci-az starts, that is a deadlock. The parent-child link already exists as a WithParentRelationship annotation, which keeps the dashboard nesting without the early resolution.
  • When floci-az runs in a container, its namespace API currently reports the sidecar's container-internal ports rather than the published host ports, so discovery through the API needs the new amqpHostPort / amqpsHostPort fields from floci-io/floci-az#592 / floci-io/floci-az#593 and an image tag bump here.
Screenshots

N/A

IDE and version

Other

IDE version

Aspire CLI / Aspire.AppHost.Sdk 13.6.4

Nuget packages

CommunityToolkit.Aspire.Hosting.Floci (current main)
Image: floci/floci-az:0.15.0
Docker Desktop on Windows 11; also Linux CI runners

Additional context

Related: #2240, #2241.

Help us help you

Yes, I'd like to be assigned to work on this item

Lingua principale
C#
Stelle
632
Fork
204
Merge medio
2g 13h
PR unite (30g)
92

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di CommunityToolkit/Aspire

Tutte le issue di CommunityToolkit/Aspire

Issue simili

Altre issue su C#

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.