Loading nixos/modules/services/networking/ncps.nix +101 −100 Original line number Diff line number Diff line Loading @@ -18,119 +18,110 @@ let ]; ncpsWrapper = pkgs.writeShellScript "ncps-wrapper" '' ${lib.optionalString (cfg.cache.secretKeyPath != null) '' export CACHE_SECRET_KEY_PATH="$CREDENTIALS_DIRECTORY/secretKey" ''} ${lib.optionalString (cfg.cache.storage.s3 != null) '' export CACHE_STORAGE_S3_ACCESS_KEY_ID="$(cat "$CREDENTIALS_DIRECTORY/s3AccessKeyId")" export CACHE_STORAGE_S3_SECRET_ACCESS_KEY="$(cat "$CREDENTIALS_DIRECTORY/s3SecretAccessKey")" ''} ${lib.optionalString (cfg.cache.redis != null && cfg.cache.redis.passwordFile != null) '' export CACHE_REDIS_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/redisPassword")" ''} ${lib.optionalString (cfg.cache.redis != null) ( if cfg.cache.redis.passwordFile != null then ''export CACHE_REDIS_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/redisPassword")"'' else if cfg.cache.redis.password != null then ''export CACHE_REDIS_PASSWORD="${cfg.cache.redis.password}"'' else "" )} ${lib.optionalString (cfg.cache.databaseURLFile != null) '' export CACHE_DATABASE_URL="$(cat "$CREDENTIALS_DIRECTORY/databaseURL")" ''} exec ${lib.getExe cfg.package} "$@" ''; globalFlags = lib.concatStringsSep " " ( [ "--log-level='${cfg.logLevel}'" ] ++ (lib.optionals cfg.openTelemetry.enable ( [ "--otel-enabled" ] ++ (lib.optional ( cfg.openTelemetry.grpcURL != null ) "--otel-grpc-url='${cfg.openTelemetry.grpcURL}'") )) ++ (lib.optional cfg.prometheus.enable "--prometheus-enabled") ++ (lib.optional (!cfg.analytics.reporting.enable) "--analytics-reporting-enabled=false") ++ (lib.optional cfg.analytics.reporting.samples "--analytics-reporting-samples") ); exec ${lib.getExe cfg.package} --config "${configFile}" "$@" ''; serveFlags = lib.concatStringsSep " " ( [ "--cache-hostname='${cfg.cache.hostName}'" "--cache-lock-backend='${cfg.cache.lock.backend}'" "--cache-temp-path='${cfg.cache.tempPath}'" "--server-addr='${cfg.server.addr}'" ] ++ (lib.optional (cfg.cache.databaseURL != null) "--cache-database-url='${cfg.cache.databaseURL}'") ++ (lib.optional (cfg.cache.database.pool.maxOpenConns != 0) "--cache-database-pool-max-open-conns='${builtins.toString cfg.cache.database.pool.maxOpenConns}'" ) ++ (lib.optional (cfg.cache.database.pool.maxIdleConns != 0) "--cache-database-pool-max-idle-conns='${builtins.toString cfg.cache.database.pool.maxIdleConns}'" settings = { log.level = cfg.logLevel; opentelemetry = lib.optionalAttrs cfg.openTelemetry.enable { enabled = true; grpc-url = cfg.openTelemetry.grpcURL; }; prometheus = lib.optionalAttrs cfg.prometheus.enable { enabled = true; }; analytics.reporting = { enabled = cfg.analytics.reporting.enable; samples = cfg.analytics.reporting.samples; }; server.addr = cfg.server.addr; cache = { allow-delete-verb = cfg.cache.allowDeleteVerb; allow-put-verb = cfg.cache.allowPutVerb; hostname = cfg.cache.hostName; database-url = cfg.cache.databaseURL; database.pool = { max-open-conns = cfg.cache.database.pool.maxOpenConns; max-idle-conns = cfg.cache.database.pool.maxIdleConns; }; max-size = cfg.cache.maxSize; lru = { schedule = cfg.cache.lru.schedule; timezone = cfg.cache.lru.scheduleTimeZone; }; sign-narinfo = cfg.cache.signNarinfo; storage = if cfg.cache.storage.s3 != null then { s3 = { bucket = cfg.cache.storage.s3.bucket; endpoint = cfg.cache.storage.s3.endpoint; region = cfg.cache.storage.s3.region; force-path-style = cfg.cache.storage.s3.forcePathStyle; }; } else { local = cfg.cache.storage.local; }; temp-path = cfg.cache.tempPath; netrc-file = cfg.netrcFile; upstream = { urls = cfg.cache.upstream.urls; public-keys = cfg.cache.upstream.publicKeys; dialer-timeout = cfg.cache.upstream.dialerTimeout; response-header-timeout = cfg.cache.upstream.responseHeaderTimeout; }; lock = { backend = cfg.cache.lock.backend; redis.key-prefix = cfg.cache.lock.redisKeyPrefix; postgres.key-prefix = cfg.cache.lock.postgresKeyPrefix; download-lock-ttl = cfg.cache.lock.downloadTTL; lru-lock-ttl = cfg.cache.lock.lruTTL; retry = { max-attempts = cfg.cache.lock.retry.maxAttempts; initial-delay = cfg.cache.lock.retry.initialDelay; max-delay = cfg.cache.lock.retry.maxDelay; jitter = cfg.cache.lock.retry.jitter; }; allow-degraded-mode = cfg.cache.lock.allowDegradedMode; }; redis = lib.optionalAttrs (cfg.cache.redis != null) { addrs = cfg.cache.redis.addresses; db = cfg.cache.redis.database; username = cfg.cache.redis.username; use-tls = cfg.cache.redis.useTLS; pool-size = cfg.cache.redis.poolSize; }; }; }; configFile = pkgs.writeText "ncps-config.json" ( builtins.toJSON ( lib.filterAttrsRecursive (_: v: v != null && v != { } && v != "" && v != [ ]) settings ) ++ (lib.optionals (cfg.cache.redis != null) ( [ "--cache-redis-db='${builtins.toString cfg.cache.redis.database}'" "--cache-redis-pool-size='${builtins.toString cfg.cache.redis.poolSize}'" ] ++ (lib.forEach cfg.cache.redis.addresses (addr: "--cache-redis-addrs='${addr}'")) ++ (lib.optional ( cfg.cache.redis.username != null ) "--cache-redis-username='${cfg.cache.redis.username}'") ++ (lib.optional ( cfg.cache.redis.password != null ) "--cache-redis-password='${cfg.cache.redis.password}'") ++ (lib.optional cfg.cache.redis.useTLS "--cache-redis-use-tls") )) ++ (lib.optional ( cfg.cache.storage.s3 == null ) "--cache-storage-local='${cfg.cache.storage.local}'") ++ (lib.optionals (cfg.cache.storage.s3 != null) ( [ "--cache-storage-s3-bucket='${cfg.cache.storage.s3.bucket}'" "--cache-storage-s3-endpoint='${cfg.cache.storage.s3.endpoint}'" ] ++ (lib.optional cfg.cache.storage.s3.forcePathStyle "--cache-storage-s3-force-path-style") ++ (lib.optional ( cfg.cache.storage.s3.region != null ) "--cache-storage-s3-region='${cfg.cache.storage.s3.region}'") )) ++ (lib.optional cfg.cache.allowDeleteVerb "--cache-allow-delete-verb") ++ (lib.optional cfg.cache.allowPutVerb "--cache-allow-put-verb") ++ (lib.optional (cfg.cache.maxSize != null) "--cache-max-size='${cfg.cache.maxSize}'") ++ (lib.optionals (cfg.cache.lru.schedule != null) [ "--cache-lru-schedule='${cfg.cache.lru.schedule}'" "--cache-lru-schedule-timezone='${cfg.cache.lru.scheduleTimeZone}'" ]) ++ (lib.optional ( cfg.cache.lock.redisKeyPrefix != "ncps:lock:" ) "--cache-lock-redis-key-prefix='${cfg.cache.lock.redisKeyPrefix}'") ++ (lib.optional ( cfg.cache.lock.postgresKeyPrefix != "ncps:lock:" ) "--cache-lock-postgres-key-prefix='${cfg.cache.lock.postgresKeyPrefix}'") ++ (lib.optional ( cfg.cache.lock.downloadTTL != "5m0s" ) "--cache-lock-download-ttl='${cfg.cache.lock.downloadTTL}'") ++ (lib.optional ( cfg.cache.lock.lruTTL != "30m0s" ) "--cache-lock-lru-ttl='${cfg.cache.lock.lruTTL}'") ++ (lib.optional ( cfg.cache.lock.retry.maxAttempts != 3 ) "--cache-lock-retry-max-attempts='${builtins.toString cfg.cache.lock.retry.maxAttempts}'") ++ (lib.optional ( cfg.cache.lock.retry.initialDelay != "100ms" ) "--cache-lock-retry-initial-delay='${cfg.cache.lock.retry.initialDelay}'") ++ (lib.optional ( cfg.cache.lock.retry.maxDelay != "2s" ) "--cache-lock-retry-max-delay='${cfg.cache.lock.retry.maxDelay}'") ++ (lib.optional (!cfg.cache.lock.retry.jitter) "--cache-lock-retry-jitter='false'") ++ (lib.optional cfg.cache.lock.allowDegradedMode "--cache-lock-allow-degraded-mode") ++ (lib.optional (cfg.cache.secretKeyPath != null) "--cache-secret-key-path='%d/secretKey'") ++ (lib.optional (!cfg.cache.signNarinfo) "--cache-sign-narinfo='false'") ++ (lib.optional ( cfg.cache.upstream.dialerTimeout != null ) "--cache-upstream-dialer-timeout='${cfg.cache.upstream.dialerTimeout}'") ++ (lib.optional ( cfg.cache.upstream.responseHeaderTimeout != null ) "--cache-upstream-response-header-timeout='${cfg.cache.upstream.responseHeaderTimeout}'") ++ (lib.forEach cfg.cache.upstream.publicKeys (pk: "--cache-upstream-public-key='${pk}'")) ++ (lib.forEach cfg.cache.upstream.urls (url: "--cache-upstream-url='${url}'")) ++ (lib.optional (cfg.netrcFile != null) "--netrc-file='${cfg.netrcFile}'") ); isSqlite = cfg.cache.databaseURL != null && lib.strings.hasPrefix "sqlite:" cfg.cache.databaseURL; Loading Loading @@ -300,6 +291,16 @@ in description = '' Lock backend to use: 'local' (single instance), 'redis' (distributed), 'postgres' (distributed, requires PostgreSQL). Advisory Locks and Connection Pools: If you use PostgreSQL as your distributed lock backend, each active lock consumes a dedicated connection from the pool. A single request can consume up to 3 connections simultaneously. To avoid deadlocks under concurrent load, ensure {option}`services.ncps.cache.database.pool.maxOpenConns` is significantly higher than your expected concurrency (at least 50-100 is recommended). ''; }; Loading Loading @@ -691,7 +692,7 @@ in serviceConfig = lib.mkMerge [ { ExecStart = "${ncpsWrapper} ${globalFlags} serve ${serveFlags}"; ExecStart = "${ncpsWrapper} serve"; User = "ncps"; Group = "ncps"; Restart = "on-failure"; Loading Loading
nixos/modules/services/networking/ncps.nix +101 −100 Original line number Diff line number Diff line Loading @@ -18,119 +18,110 @@ let ]; ncpsWrapper = pkgs.writeShellScript "ncps-wrapper" '' ${lib.optionalString (cfg.cache.secretKeyPath != null) '' export CACHE_SECRET_KEY_PATH="$CREDENTIALS_DIRECTORY/secretKey" ''} ${lib.optionalString (cfg.cache.storage.s3 != null) '' export CACHE_STORAGE_S3_ACCESS_KEY_ID="$(cat "$CREDENTIALS_DIRECTORY/s3AccessKeyId")" export CACHE_STORAGE_S3_SECRET_ACCESS_KEY="$(cat "$CREDENTIALS_DIRECTORY/s3SecretAccessKey")" ''} ${lib.optionalString (cfg.cache.redis != null && cfg.cache.redis.passwordFile != null) '' export CACHE_REDIS_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/redisPassword")" ''} ${lib.optionalString (cfg.cache.redis != null) ( if cfg.cache.redis.passwordFile != null then ''export CACHE_REDIS_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/redisPassword")"'' else if cfg.cache.redis.password != null then ''export CACHE_REDIS_PASSWORD="${cfg.cache.redis.password}"'' else "" )} ${lib.optionalString (cfg.cache.databaseURLFile != null) '' export CACHE_DATABASE_URL="$(cat "$CREDENTIALS_DIRECTORY/databaseURL")" ''} exec ${lib.getExe cfg.package} "$@" ''; globalFlags = lib.concatStringsSep " " ( [ "--log-level='${cfg.logLevel}'" ] ++ (lib.optionals cfg.openTelemetry.enable ( [ "--otel-enabled" ] ++ (lib.optional ( cfg.openTelemetry.grpcURL != null ) "--otel-grpc-url='${cfg.openTelemetry.grpcURL}'") )) ++ (lib.optional cfg.prometheus.enable "--prometheus-enabled") ++ (lib.optional (!cfg.analytics.reporting.enable) "--analytics-reporting-enabled=false") ++ (lib.optional cfg.analytics.reporting.samples "--analytics-reporting-samples") ); exec ${lib.getExe cfg.package} --config "${configFile}" "$@" ''; serveFlags = lib.concatStringsSep " " ( [ "--cache-hostname='${cfg.cache.hostName}'" "--cache-lock-backend='${cfg.cache.lock.backend}'" "--cache-temp-path='${cfg.cache.tempPath}'" "--server-addr='${cfg.server.addr}'" ] ++ (lib.optional (cfg.cache.databaseURL != null) "--cache-database-url='${cfg.cache.databaseURL}'") ++ (lib.optional (cfg.cache.database.pool.maxOpenConns != 0) "--cache-database-pool-max-open-conns='${builtins.toString cfg.cache.database.pool.maxOpenConns}'" ) ++ (lib.optional (cfg.cache.database.pool.maxIdleConns != 0) "--cache-database-pool-max-idle-conns='${builtins.toString cfg.cache.database.pool.maxIdleConns}'" settings = { log.level = cfg.logLevel; opentelemetry = lib.optionalAttrs cfg.openTelemetry.enable { enabled = true; grpc-url = cfg.openTelemetry.grpcURL; }; prometheus = lib.optionalAttrs cfg.prometheus.enable { enabled = true; }; analytics.reporting = { enabled = cfg.analytics.reporting.enable; samples = cfg.analytics.reporting.samples; }; server.addr = cfg.server.addr; cache = { allow-delete-verb = cfg.cache.allowDeleteVerb; allow-put-verb = cfg.cache.allowPutVerb; hostname = cfg.cache.hostName; database-url = cfg.cache.databaseURL; database.pool = { max-open-conns = cfg.cache.database.pool.maxOpenConns; max-idle-conns = cfg.cache.database.pool.maxIdleConns; }; max-size = cfg.cache.maxSize; lru = { schedule = cfg.cache.lru.schedule; timezone = cfg.cache.lru.scheduleTimeZone; }; sign-narinfo = cfg.cache.signNarinfo; storage = if cfg.cache.storage.s3 != null then { s3 = { bucket = cfg.cache.storage.s3.bucket; endpoint = cfg.cache.storage.s3.endpoint; region = cfg.cache.storage.s3.region; force-path-style = cfg.cache.storage.s3.forcePathStyle; }; } else { local = cfg.cache.storage.local; }; temp-path = cfg.cache.tempPath; netrc-file = cfg.netrcFile; upstream = { urls = cfg.cache.upstream.urls; public-keys = cfg.cache.upstream.publicKeys; dialer-timeout = cfg.cache.upstream.dialerTimeout; response-header-timeout = cfg.cache.upstream.responseHeaderTimeout; }; lock = { backend = cfg.cache.lock.backend; redis.key-prefix = cfg.cache.lock.redisKeyPrefix; postgres.key-prefix = cfg.cache.lock.postgresKeyPrefix; download-lock-ttl = cfg.cache.lock.downloadTTL; lru-lock-ttl = cfg.cache.lock.lruTTL; retry = { max-attempts = cfg.cache.lock.retry.maxAttempts; initial-delay = cfg.cache.lock.retry.initialDelay; max-delay = cfg.cache.lock.retry.maxDelay; jitter = cfg.cache.lock.retry.jitter; }; allow-degraded-mode = cfg.cache.lock.allowDegradedMode; }; redis = lib.optionalAttrs (cfg.cache.redis != null) { addrs = cfg.cache.redis.addresses; db = cfg.cache.redis.database; username = cfg.cache.redis.username; use-tls = cfg.cache.redis.useTLS; pool-size = cfg.cache.redis.poolSize; }; }; }; configFile = pkgs.writeText "ncps-config.json" ( builtins.toJSON ( lib.filterAttrsRecursive (_: v: v != null && v != { } && v != "" && v != [ ]) settings ) ++ (lib.optionals (cfg.cache.redis != null) ( [ "--cache-redis-db='${builtins.toString cfg.cache.redis.database}'" "--cache-redis-pool-size='${builtins.toString cfg.cache.redis.poolSize}'" ] ++ (lib.forEach cfg.cache.redis.addresses (addr: "--cache-redis-addrs='${addr}'")) ++ (lib.optional ( cfg.cache.redis.username != null ) "--cache-redis-username='${cfg.cache.redis.username}'") ++ (lib.optional ( cfg.cache.redis.password != null ) "--cache-redis-password='${cfg.cache.redis.password}'") ++ (lib.optional cfg.cache.redis.useTLS "--cache-redis-use-tls") )) ++ (lib.optional ( cfg.cache.storage.s3 == null ) "--cache-storage-local='${cfg.cache.storage.local}'") ++ (lib.optionals (cfg.cache.storage.s3 != null) ( [ "--cache-storage-s3-bucket='${cfg.cache.storage.s3.bucket}'" "--cache-storage-s3-endpoint='${cfg.cache.storage.s3.endpoint}'" ] ++ (lib.optional cfg.cache.storage.s3.forcePathStyle "--cache-storage-s3-force-path-style") ++ (lib.optional ( cfg.cache.storage.s3.region != null ) "--cache-storage-s3-region='${cfg.cache.storage.s3.region}'") )) ++ (lib.optional cfg.cache.allowDeleteVerb "--cache-allow-delete-verb") ++ (lib.optional cfg.cache.allowPutVerb "--cache-allow-put-verb") ++ (lib.optional (cfg.cache.maxSize != null) "--cache-max-size='${cfg.cache.maxSize}'") ++ (lib.optionals (cfg.cache.lru.schedule != null) [ "--cache-lru-schedule='${cfg.cache.lru.schedule}'" "--cache-lru-schedule-timezone='${cfg.cache.lru.scheduleTimeZone}'" ]) ++ (lib.optional ( cfg.cache.lock.redisKeyPrefix != "ncps:lock:" ) "--cache-lock-redis-key-prefix='${cfg.cache.lock.redisKeyPrefix}'") ++ (lib.optional ( cfg.cache.lock.postgresKeyPrefix != "ncps:lock:" ) "--cache-lock-postgres-key-prefix='${cfg.cache.lock.postgresKeyPrefix}'") ++ (lib.optional ( cfg.cache.lock.downloadTTL != "5m0s" ) "--cache-lock-download-ttl='${cfg.cache.lock.downloadTTL}'") ++ (lib.optional ( cfg.cache.lock.lruTTL != "30m0s" ) "--cache-lock-lru-ttl='${cfg.cache.lock.lruTTL}'") ++ (lib.optional ( cfg.cache.lock.retry.maxAttempts != 3 ) "--cache-lock-retry-max-attempts='${builtins.toString cfg.cache.lock.retry.maxAttempts}'") ++ (lib.optional ( cfg.cache.lock.retry.initialDelay != "100ms" ) "--cache-lock-retry-initial-delay='${cfg.cache.lock.retry.initialDelay}'") ++ (lib.optional ( cfg.cache.lock.retry.maxDelay != "2s" ) "--cache-lock-retry-max-delay='${cfg.cache.lock.retry.maxDelay}'") ++ (lib.optional (!cfg.cache.lock.retry.jitter) "--cache-lock-retry-jitter='false'") ++ (lib.optional cfg.cache.lock.allowDegradedMode "--cache-lock-allow-degraded-mode") ++ (lib.optional (cfg.cache.secretKeyPath != null) "--cache-secret-key-path='%d/secretKey'") ++ (lib.optional (!cfg.cache.signNarinfo) "--cache-sign-narinfo='false'") ++ (lib.optional ( cfg.cache.upstream.dialerTimeout != null ) "--cache-upstream-dialer-timeout='${cfg.cache.upstream.dialerTimeout}'") ++ (lib.optional ( cfg.cache.upstream.responseHeaderTimeout != null ) "--cache-upstream-response-header-timeout='${cfg.cache.upstream.responseHeaderTimeout}'") ++ (lib.forEach cfg.cache.upstream.publicKeys (pk: "--cache-upstream-public-key='${pk}'")) ++ (lib.forEach cfg.cache.upstream.urls (url: "--cache-upstream-url='${url}'")) ++ (lib.optional (cfg.netrcFile != null) "--netrc-file='${cfg.netrcFile}'") ); isSqlite = cfg.cache.databaseURL != null && lib.strings.hasPrefix "sqlite:" cfg.cache.databaseURL; Loading Loading @@ -300,6 +291,16 @@ in description = '' Lock backend to use: 'local' (single instance), 'redis' (distributed), 'postgres' (distributed, requires PostgreSQL). Advisory Locks and Connection Pools: If you use PostgreSQL as your distributed lock backend, each active lock consumes a dedicated connection from the pool. A single request can consume up to 3 connections simultaneously. To avoid deadlocks under concurrent load, ensure {option}`services.ncps.cache.database.pool.maxOpenConns` is significantly higher than your expected concurrency (at least 50-100 is recommended). ''; }; Loading Loading @@ -691,7 +692,7 @@ in serviceConfig = lib.mkMerge [ { ExecStart = "${ncpsWrapper} ${globalFlags} serve ${serveFlags}"; ExecStart = "${ncpsWrapper} serve"; User = "ncps"; Group = "ncps"; Restart = "on-failure"; Loading