foundationdb-dotnet-client icon indicating copy to clipboard operation
foundationdb-dotnet-client copied to clipboard

Adding support for Aspire Preview

Open KrzysFR opened this issue 7 months ago • 0 comments

We live in interesting times: https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview

Documentation on how to create a new component is a bit scarce for the moment, but I was able to create a first prototype of a FoundationDB aspire component.

Integration with the AppHost and projects

Example of an AppHost:

private static void Main(string[] args)
{
    var builder = DistributedApplication.CreateBuilder(args);

    // we need a local FoundationDB cluster
    var fdb = builder
        .AddFdbCluster("fdb", apiVersion: 720, root: "/Sandbox/MySuperApp", clusterVersion: "7.2.5", rollForward: FdbVersionPolicy.Exact)
        .WithReplicas(1);

    // We have an API backend, which uses the fdb cluster
    var backend = builder
        .AddProject<Projects.AwesomeWebApiBackend>("backend")
        .WithReference(fdb); // <-- hooks up the cluster connection string with this project

    // We have a front end (like a Blazor web app) that will talk the backend, but will NOT do any request with the fdb cluser
    var agent = builder
        .AddProject<Projects.AwesomeBlazorWebApp>("agent")
        .WithReference(backend);

    builder.Build().Run();
}

Here, we will setup a "transient" fdb cluster using a locally started foundationdb docker image (https://hub.docker.com/r/foundationdb/foundationdb), using a local instance of Docker (support windows, linux, macos).

The "connection string" that we generate for the cluster includes the selected API version (720), the root path of the application in the cluster (/Sandbox/MySuperApp), and we select an exact version for the FoundationDB cluster (7.2.5).

This connection string will be available to the backend process (via environment variables) which will automatically configure a local FdbDatabaseProvider with the same settings.

In the backend startup logic, instead of adding a regular fdb database provider, we can use the new extension method:

        private static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // setup Aspire services...
            builder.AddServiceDefaults();
            builder.Services.AddProblemDetails();

            // hookup the FoundationDB component
            builder.AddFoundationDb("fdb"); // "fdb" is the same name we used in AddFdbCluster(...) in the AppHost above.

            // ...rest of the startup logic....
}

At runtime

Running the AppHost inside Visual Studio (F5) we see that a new docker container is started and listening on port 4550: image

And it is also visible in the Aspire Dashboard: image

The docker image is configured with the following variables: image

The connection string is injected inside the 'backend' process via the ConnectionStrings__fdb variable with value ApiVersion=720;Root=/Sandbox/Syracuse;ClusterFileContents=docker:[email protected]:4550;ClientVersion=7.2.5, which is then picked up during startup to setup the database provider: image

Issues

I encountered the following issues:

Cannot override the ENTRYPOINT of an Aspire container resource

The foundationdb docker starts the fdb.bash entrypoint which will connect the node to an existing cluster.

For local, single-node development, we need to run configure new single ... at least once to initialize the cluster. The docker image contains another alternative entrypoint fdb-single.bash which would do that, but Aspire (preview2) does not expose a way to override the ENTRYPOINT. Snooping in the code, there is a "Command" property on the ContainerSpec object, but I don't find any obvious way to change this value (the value is not assigned anywhere in the current Aspire preview).

Currently, I have to copy the cluster file somewhere, and run fdbcli -C path/to/file.cluster --exec "configure new single memory" manually from the host.

The docker is stopped and restarted on every start of the application, which means all data is lost. I'm adding volume mount from /var/fdb/data to a temp folder, but doing this with Docker for Windows, and using the wsl2 backend, crashes the fdbserver (probably having a volume mount on a docker linux image, through docker, to a windows NTFS partition is creating some issues).

I'm using a named volume mount instead, to work around this issue, but I can only one per container.

Custom cluster file contents requires a temp local file

The docker image will accept a FDB_CLUSTER_CONTENTS parameter, but there is nothing similar in the fdb C api, which means the backend process in our example above needs to write a file somewhere on the disk, with docker:[email protected]:4550 and then pass the path to this file when connecting to the database.

This creates a few issues like requiring write permissions on the local folder where the binary are deployed, OR find a temp folder that is writable.

No easy way to manage version roll forwar policy with docker image

The foundationdb docker image has a latest tag, and then one tag per version, but there is not "latest per minor or major version" tag, like for example "7.2-latest" or "7.x-latest" which could be used to easily select the last compatible version.

For now, I have hardcoded a set of versions for 7.1 / 7.2 and 7.3 in the code, but this is not ideal.

I tried looking at the Docker API to fetch the list of tags, but

  1. this API has already changed (now in v2)
  2. the code path in the Aspire AppHost that has to configure the resources is NOT async, and doing HTTP requests to the cloud would cause issues
  3. the CI server would need access to the cloud,
  4. it could be throttled by DockerHub

Requires Aspire preview 2 which is not public yet

There is a nasty bug in preview 1 where it will not use the correct service ports (and instead use random ports), which has been fixed in the repo, but not yet available on the public nuget feed.

I'm currently using the daily build feed that has this issue fixed, see https://github.com/dotnet/aspire/blob/main/docs/using-latest-daily.md

You MUST update the aspire workload and tooling, or else it will fail at runtime (dcp reconcilier errors).

KrzysFR avatar Nov 24 '23 17:11 KrzysFR