dotnet / aspire

An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
https://learn.microsoft.com/dotnet/aspire
MIT License
3.49k stars 375 forks source link

Unreliable start up process on apphost #3857

Open hades200082 opened 3 months ago

hades200082 commented 3 months ago

First, let me say, I'm using Rider, not Visual Studio.

When restarting the AppHost project I've been seeing some odd behaviour.

Sometimes it works fine.

Sometimes it just hangs on "D:\Program Files\JetBrains\Rider\plugins\dpa\DotFiles\JetBrains.DPA.Runner.exe" --handle=24252 --backend-pid=2280 --etw-collect-flags=67108622 --detach-event-name=dpa.detach.24252 D:/Projects/Personal/qs-reborn/QSRebornAPI/AppHost/bin/Debug/net8.0/AppHost.exe

No other logs. When this happens, if I restart it I then get a different issue...

image

It seems to start up, but only one container is shown as "running" in the Aspire dashboard... which is also super odd because Docker shows no containers running. It's always the Azurite container that shows as running when this happens.

Console logs:

"D:\Program Files\JetBrains\Rider\plugins\dpa\DotFiles\JetBrains.DPA.Runner.exe" --handle=24252 --backend-pid=2280 --etw-collect-flags=67108622 --detach-event-name=dpa.detach.24252 D:/Projects/Personal/qs-reborn/QSRebornAPI/AppHost/bin/Debug/net8.0/AppHost.exe
info: Aspire.Hosting.Azure.AzureProvisioner[0]
      Azure resource connection strings saved to user secrets.
info: Aspire.Hosting.DistributedApplication[0]
      Aspire version: 8.0.0-preview.5.24201.12+1b627b7d5d399d4f9118366e7611e11e56de4554
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
      Application host directory is: D:\Projects\Personal\qs-reborn\QSRebornAPI\AppHost
info: Aspire.Hosting.DistributedApplication[0]
      Now listening on: https://localhost:17079

There are no other logs.

When I restart it for a third time it will start up fine. I've been able to reproduce this behaviour fairly reliably.

// AppHost.Program.cs
using Microsoft.Extensions.Configuration;

var builder = DistributedApplication.CreateBuilder(args);

// Storage provider
// AzureBlobStorage | Sftp | LocalDisk // Todo : Add others when available: AmazonS3 | GoogleCloudStorage
var storageType = builder.Configuration["StorageType"];
var sftpConnStr = builder.Configuration.GetConnectionString("Storage_Sftp");
var localConnStr = builder.Configuration.GetConnectionString("Storage_LocalDiskPath");

var storage = storageType switch
{
    "AzureBlobStorage" => builder.AddAzureStorage("Storage")
        .RunAsEmulator(ct =>
        {
            ct.WithOtlpExporter();
            ct.WithDataVolume("AppHost-Azurite-data");
        })
        .AddBlobs("blob"),

    "AmazonS3" => throw new NotImplementedException("AmazonS3 is not yet supported by .Net Aspire"),

    "GoogleCloudStorage" => throw new NotImplementedException("GoogleCloudStorage is not yet supported by .Net Aspire"),

    "Sftp" => string.IsNullOrWhiteSpace(sftpConnStr) 
        ? throw new ArgumentException("Connection string Storage_Sftp must be set to use Sftp as storage") 
        : builder.AddConnectionString("Storage_Sftp", "Storage"),

    "LocalDisk" => string.IsNullOrWhiteSpace(localConnStr)
        ? throw new ArgumentException("Connection string Storage_LocalDiskPath must be set to use LocalDisk as storage")
        : builder.AddConnectionString("Storage_LocalDiskPath", "Storage"),

    _ => throw new ArgumentOutOfRangeException(storageType, "StorageType must be one of AzureBlobStorage | AmazonS3 | GoogleCloudStorage | Sftp | LocalDisk")
};

// AMQP provider
// AzureServiceBus | RabbitMQ // TODO: Add others as they become available such as AmazonSQS 
var amqpType = builder.Configuration["AMQPTransportType"];
IResourceBuilder<IResourceWithConnectionString> amqp = amqpType switch
{
    "RabbitMQ" => builder.AddRabbitMQ("AMQPTransport", password: builder.AddParameter("AMQPPassword", true))
            .WithManagementPlugin()
            .WithImage("masstransit/rabbitmq")
            .WithDataVolume("AppHost-RabbitMQ-data"),

    "AzureServiceBus" => throw new NotImplementedException("AzureServiceBus is not yet supported"),

    "AmazonSQS" => throw new NotImplementedException("AmazonSQS is not yet supported by .Net Aspire")
};

// SQL Database
var databaseType = builder.Configuration["DatabaseType"];
var databaseName = builder.Configuration["DatabaseName"] 
                   ?? "appdata";

IResourceBuilder<IResourceWithConnectionString> db;
var dbPassword = builder.AddParameter("DatabasePassword", true);
switch (databaseType)
{
    case "MSSQL":
        db = builder.AddSqlServer("Database", password: dbPassword)
            .WithDataVolume("AppHost-MSSQL-data").AddDatabase(databaseName);
        break;
    case "MySQL" or "MariaDB":
        db = builder.AddMySql("Database", password: dbPassword)
            .WithDataVolume("AppHost-MySQL-data")
            .WithPhpMyAdmin()
            .AddDatabase(databaseName);
        break;
    case "Postgres" or "PostgreSQL":
        db = builder.AddPostgres("Database", password: dbPassword)
            .WithDataVolume("AppHost-PostgreSQL-data")
            .WithPgAdmin()
            .AddDatabase(databaseName);
        break;
    default:
        throw new ArgumentOutOfRangeException(nameof(databaseType));
}

var redis = builder.AddRedis("Redis")
    .WithDataVolume()
    .WithRedisCommander();

var api = builder.AddProject<Projects.Application_Api_Host>("api")
    .WithEnvironment("StorageType", storageType)
    .WithReference(storage, "Storage")
    .WithEnvironment("AMQPTransportType", amqpType)
    .WithReference(amqp, "AMQP")
    .WithEnvironment("DatabaseType", databaseType)
    .WithReference(db, $"Database_{databaseName}")
    .WithReference(redis, "Redis");

var worker = builder.AddProject<Projects.Application_Worker_Host>("worker")
    .WithEnvironment("StorageType", storageType)
    .WithReference(storage, "Storage")
    .WithEnvironment("AMQPTransportType", amqpType)
    .WithReference(amqp, "AMQP")
    .WithEnvironment("DatabaseType", databaseType)
    .WithReference(db, $"Database_{databaseName}");

builder.Build().Run();
// AppHost/appSettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "DatabaseType": "PostgreSQL",
  "AMQPTransportType": "RabbitMQ",
  "StorageType": "AzureBlobStorage",
  "Parameters": {
    "AMQPPassword": "guest",
    "GrafanaPassword": "admin",
    "DatabasePassword": "redacted"
  }
}

api: A WebAPI using controllers worker: a web application that registers consumers for the AMQP

Both projects use MassTransit to work with the AMQP transport.

asimmon commented 3 months ago

I don't know what's going on here but you changing the logs severity could print more details about this behavior:

// AppHost/appSettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Aspire": "Trace", // add this
    }
  },
  // ...
}
hades200082 commented 1 month ago

I've not seen this behaviour on newer versions so maybe fixed already?