about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVova Kryachko <v.kryachko@gmail.com>2024-10-29T18·00-0400
committerVladimir Kryachko <v.kryachko@gmail.com>2024-10-30T15·00+0000
commit9e294db8206ee8f8ed99887367222c2197c5c83c (patch)
treea36c6a2cc819024c0fa8fd12d9970901c2f9c3fc
parenteb2ca5f07991cc526b22b1701896f40d38c46d46 (diff)
feat(tvix/nix-daemon): Initial skeleton for nix-daemon r/8878
The goal is to create a drop-in replacement nix-daemon that nix-cpp can
use as a `daemon` store.

Change-Id: Ie092047dcc6a24a3b8d8d1b808f3e6fd2c493bf2
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12711
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
-rw-r--r--tvix/Cargo.lock17
-rw-r--r--tvix/Cargo.nix76
-rw-r--r--tvix/Cargo.toml1
-rw-r--r--tvix/nix-daemon/Cargo.toml24
-rw-r--r--tvix/nix-daemon/default.nix5
-rw-r--r--tvix/nix-daemon/src/bin/nix-daemon.rs90
6 files changed, 213 insertions, 0 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock
index f9c5907e20ef..2984479c18ef 100644
--- a/tvix/Cargo.lock
+++ b/tvix/Cargo.lock
@@ -2344,6 +2344,23 @@ dependencies = [
 ]
 
 [[package]]
+name = "nix-daemon"
+version = "0.1.0"
+dependencies = [
+ "async-trait",
+ "clap",
+ "futures",
+ "mimalloc",
+ "nix-compat",
+ "tokio",
+ "tokio-listener",
+ "tracing",
+ "tvix-castore",
+ "tvix-store",
+ "tvix-tracing",
+]
+
+[[package]]
 name = "nohash-hasher"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix
index d0b94380ee00..749f39fb93cc 100644
--- a/tvix/Cargo.nix
+++ b/tvix/Cargo.nix
@@ -75,6 +75,16 @@ rec {
       # File a bug if you depend on any for non-debug work!
       debug = internal.debugCrate { inherit packageId; };
     };
+    "nix-daemon" = rec {
+      packageId = "nix-daemon";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "nix-daemon";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
     "tvix-build" = rec {
       packageId = "tvix-build";
       build = internal.buildRustCrateWithFeatures {
@@ -7419,6 +7429,72 @@ rec {
         features = { };
         resolvedDefaultFeatures = [ "compile-tests" ];
       };
+      "nix-daemon" = rec {
+        crateName = "nix-daemon";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "nix-daemon";
+            path = "src/bin/nix-daemon.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        src = lib.cleanSourceWith { filter = sourceFilter; src = ./nix-daemon; };
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" "env" ];
+          }
+          {
+            name = "futures";
+            packageId = "futures";
+          }
+          {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "net" "rt" "rt-multi-thread" "signal" ];
+          }
+          {
+            name = "tokio-listener";
+            packageId = "tokio-listener";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tvix-castore";
+            packageId = "tvix-castore";
+          }
+          {
+            name = "tvix-store";
+            packageId = "tvix-store";
+          }
+          {
+            name = "tvix-tracing";
+            packageId = "tvix-tracing";
+          }
+        ];
+        features = {
+          "default" = [ "otlp" ];
+          "otlp" = [ "tvix-tracing/otlp" ];
+        };
+        resolvedDefaultFeatures = [ "default" "otlp" ];
+      };
       "nohash-hasher" = rec {
         crateName = "nohash-hasher";
         version = "0.2.0";
diff --git a/tvix/Cargo.toml b/tvix/Cargo.toml
index 6b30e1196c39..19f3a2b9c521 100644
--- a/tvix/Cargo.toml
+++ b/tvix/Cargo.toml
@@ -29,6 +29,7 @@ members = [
   "nix-compat",
   "nix-compat-derive",
   "nix-compat-derive-tests",
+  "nix-daemon",
   "serde",
   "store",
   "tracing",
diff --git a/tvix/nix-daemon/Cargo.toml b/tvix/nix-daemon/Cargo.toml
new file mode 100644
index 000000000000..39b34019b424
--- /dev/null
+++ b/tvix/nix-daemon/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "nix-daemon"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+async-trait = "0.1.83"
+clap = { workspace = true, features = ["derive", "env"] }
+futures = { workspace = true }
+mimalloc = { workspace = true }
+nix-compat = { path = "../nix-compat" }
+tvix-castore = { path = "../castore" }
+tvix-store = { path = "../store" }
+tvix-tracing = { path = "../tracing" }
+tokio = { workspace = true, features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
+tokio-listener = { workspace = true }
+tracing = { workspace = true }
+
+[lints]
+workspace = true
+
+[features]
+default = ["otlp"]
+otlp = ["tvix-tracing/otlp"]
\ No newline at end of file
diff --git a/tvix/nix-daemon/default.nix b/tvix/nix-daemon/default.nix
new file mode 100644
index 000000000000..740bc851ea19
--- /dev/null
+++ b/tvix/nix-daemon/default.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+depot.tvix.crates.workspaceMembers.nix-daemon.build.override {
+  runTests = true;
+}
diff --git a/tvix/nix-daemon/src/bin/nix-daemon.rs b/tvix/nix-daemon/src/bin/nix-daemon.rs
new file mode 100644
index 000000000000..769e968309f8
--- /dev/null
+++ b/tvix/nix-daemon/src/bin/nix-daemon.rs
@@ -0,0 +1,90 @@
+use clap::Parser;
+use mimalloc::MiMalloc;
+use std::error::Error;
+use tokio::io::AsyncWriteExt;
+use tokio_listener::SystemOptions;
+use tvix_store::utils::{construct_services, ServiceUrlsGrpc};
+
+#[global_allocator]
+static GLOBAL: MiMalloc = MiMalloc;
+
+/// Run Nix-compatible store daemon backed by tvix.
+#[derive(Parser)]
+struct Cli {
+    #[clap(flatten)]
+    service_addrs: ServiceUrlsGrpc,
+
+    /// The address to listen on. Must be a unix domain socket.
+    #[clap(flatten)]
+    listen_args: tokio_listener::ListenerAddressLFlag,
+
+    #[cfg(feature = "otlp")]
+    /// Whether to configure OTLP. Set --otlp=false to disable.
+    #[arg(long, default_missing_value = "true", default_value = "true", num_args(0..=1), require_equals(true), action(clap::ArgAction::Set))]
+    otlp: bool,
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
+    let cli = Cli::parse();
+
+    let tracing_handle = {
+        let mut builder = tvix_tracing::TracingBuilder::default();
+        builder = builder.enable_progressbar();
+        #[cfg(feature = "otlp")]
+        {
+            if cli.otlp {
+                builder = builder.enable_otlp("tvix.daemon");
+            }
+        }
+        builder.build()?
+    };
+
+    tokio::select! {
+        res = tokio::signal::ctrl_c() => {
+            res?;
+            if let Err(e) = tracing_handle.force_shutdown().await {
+                eprintln!("failed to shutdown tracing: {e}");
+            }
+            Ok(())
+        },
+        res = run(cli) => {
+            if let Err(e) = tracing_handle.shutdown().await {
+                eprintln!("failed to shutdown tracing: {e}");
+            }
+            res
+        }
+    }
+}
+
+async fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+    let (_blob_service, _directory_service, _path_info_service, _nar_calculation_service) =
+        construct_services(cli.service_addrs).await?;
+
+    let listen_address = cli.listen_args.listen_address.unwrap_or_else(|| {
+        "/tmp/tvix-daemon.sock"
+            .parse()
+            .expect("invalid fallback listen address")
+    });
+
+    let mut listener = tokio_listener::Listener::bind(
+        &listen_address,
+        &SystemOptions::default(),
+        &cli.listen_args.listener_options,
+    )
+    .await?;
+
+    while let Ok((mut connection, _)) = listener.accept().await {
+        tokio::spawn(async move {
+            let ucred = connection
+                .try_borrow_unix()
+                .and_then(|u| u.peer_cred().ok());
+
+            // For now we just write the connected process credentials into the connection.
+            let _ = connection
+                .write_all(format!("Hello {:?}", ucred).as_bytes())
+                .await;
+        });
+    }
+    Ok(())
+}