Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dragonfly #6

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/templates/frappe/apps/pkgs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ let
frappix.toolsOverlay
frappix.pythonOverlay
frappix.frappeOverlay
frappix.packagesOverlay
inject
];
};
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
toolsOverlay = std.harvest self ["src" "overlays" "tools"];
pythonOverlay = std.harvest self ["src" "overlays" "python"];
frappeOverlay = std.harvest self ["src" "overlays" "frappe"];
packagesOverlay = std.harvest self ["src" "overlays" "packages"];
nixosModules = std.harvest self ["src" "nixos"];
frapper = import ./std/frapper.nix {inherit inputs;};
nvchecker = import ./std/nvchecker.nix {inherit inputs;};
Expand Down
13 changes: 13 additions & 0 deletions src/nixos.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@
];
};
};
dragonflyUds = {
meta.description = "Dragonfly db module implementing unix domain socket";
__functor = _: {
pkgs,
lib,
...
}: {
_file = ./nixos.nix;
imports = map (m: lib.modules.setDefaultModuleLocation m m) [
./nixos/dragonflydb.nix
];
};
};
testrig = {
meta.description = "The frappix nixos testrig mixin module";
__functor = _: {
Expand Down
151 changes: 151 additions & 0 deletions src/nixos/dragonflydb.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
with lib; let
cfg = config.services.dragonflydb;
dragonflydb = pkgs.dragonflydb;

settings =
{
dir = "/var/lib/dragonflydb";
keys_output_limit = cfg.keysOutputLimit;
}
// (lib.optionalAttrs (cfg.unixSocket != null) {
unixsocket = cfg.unixSocket;
port = 0;
})
// (lib.optionalAttrs (cfg.requirePass != null) {requirepass = cfg.requirePass;})
// (lib.optionalAttrs (cfg.maxMemory != null) {maxmemory = cfg.maxMemory;})
// (lib.optionalAttrs (cfg.memcachePort != null) {memcache_port = cfg.memcachePort;})
// (lib.optionalAttrs (cfg.dbNum != null) {dbnum = cfg.dbNum;})
// (lib.optionalAttrs (cfg.cacheMode != null) {cache_mode = cfg.cacheMode;});
in {
###### interface
disabledModules = [
(modulesPath + "/services/databases/dragonflydb.nix")
];
options = {
services.dragonflydb = {
enable = mkEnableOption "DragonflyDB";

user = mkOption {
type = types.str;
default = "dragonfly";
description = "The user to run DragonflyDB as";
};

unixSocket = mkOption {
type = with types; nullOr path;
default = "/run/dragonflydb/dragonfly.sock";
description = "The TCP port to accept connections.";
};

requirePass = mkOption {
type = with types; nullOr str;
default = null;
description = "Password for database";
example = "letmein!";
};

maxMemory = mkOption {
type = with types; nullOr ints.unsigned;
default = null;
description = ''
The maximum amount of memory to use for storage (in bytes).
`null` means this will be automatically set.
'';
};

memcachePort = mkOption {
type = with types; nullOr port;
default = null;
description = ''
To enable memcached compatible API on this port.
`null` means disabled.
'';
};

keysOutputLimit = mkOption {
type = types.ints.unsigned;
default = 8192;
description = ''
Maximum number of returned keys in keys command.
`keys` is a dangerous command.
We truncate its result to avoid blowup in memory when fetching too many keys.
'';
};

dbNum = mkOption {
type = with types; nullOr ints.unsigned;
default = null;
description = "Maximum number of supported databases for `select`";
};

cacheMode = mkOption {
type = with types; nullOr bool;
default = null;
description = ''
Once this mode is on, Dragonfly will evict items least likely to be stumbled
upon in the future but only when it is near maxmemory limit.
'';
};
};
};

###### implementation

config = mkIf config.services.dragonflydb.enable {
users.users = optionalAttrs (cfg.user == "dragonfly") {
dragonfly.description = "DragonflyDB server user";
dragonfly.isSystemUser = true;
dragonfly.group = "dragonfly";
};
users.groups = optionalAttrs (cfg.user == "dragonfly") {dragonfly = {};};

environment.systemPackages = [dragonflydb];

systemd.services.dragonflydb = {
description = "DragonflyDB server";

wantedBy = ["multi-user.target"];
after = ["network.target"];

serviceConfig = {
ExecStart = "${dragonflydb}/bin/dragonfly --alsologtostderr true ${builtins.concatStringsSep " " (attrsets.mapAttrsToList (n: v: "--${n} ${strings.escapeShellArg v}") settings)}";

User = cfg.user;

# Filesystem access
ReadWritePaths = [settings.dir];
# Runtime directory and mode
RuntimeDirectory = lib.removePrefix "/run/" (builtins.dirOf cfg.unixSocket);
RuntimeDirectoryMode = "0750";
# State directory and mode
StateDirectory = "dragonflydb";
StateDirectoryMode = "0700";
# Process Properties
LimitMEMLOCK = "infinity";
# Caps
CapabilityBoundingSet = "";
NoNewPrivileges = true;
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
LockPersonality = true;
RestrictAddressFamilies = "AF_UNIX";
RestrictRealtime = true;
PrivateMounts = true;
MemoryDenyWriteExecute = true;
};
};
};
}
62 changes: 39 additions & 23 deletions src/nixos/main.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ in {
#
# Interface
#
imports = [
./dragonflydb.nix # with unixsocket
];
options = {
services.frappe = {
enable = mkEnableOption (mdDoc "frappe");
useDragonfly = mkEnableOption (mdDoc "Used dragonfly db instead of redis (supporst redisearch-like api)");

package = mkOption {
type = types.package;
Expand Down Expand Up @@ -159,8 +163,8 @@ in {
*/
webSocket = mkInternal;
socketIOSocket = mkInternal;
redisCacheSocket = mkInternal;
redisQueueSocket = mkInternal;
cacheSocket = mkInternal;
queueSocket = mkInternal;
mariadbSocket = mkInternal;
benchDirectory = mkInternal;
combinedAssets = mkInternal;
Expand Down Expand Up @@ -205,7 +209,7 @@ in {
script = ''
set -euo pipefail

redis-cli -s "${cfg.redisCacheSocket}" del assets_json
redis-cli -s "${cfg.cacheSocket}" del assets_json
'';
description = "Asset cache clearing on new deployments for project: ${cfg.project}";
};
Expand Down Expand Up @@ -240,8 +244,11 @@ in {
# backfill internal interface
# - well-known (socket) paths
frappe.mariadbSocket = "/run/mysqld/mysqld.sock";
frappe.redisQueueSocket = "/run/redis-${cfg.project}-queue/redis.sock";
frappe.redisCacheSocket = "/run/redis-${cfg.project}-cache/redis.sock";
frappe.queueSocket = "/run/redis-${cfg.project}-queue/redis.sock";
frappe.cacheSocket =
if (!cfg.useDragonfly)
then "/run/redis-${cfg.project}-cache/redis.sock"
else "/run/dragonflydb-${cfg.project}-cache/dragonfly.sock";
frappe.webSocket = "/run/${cfg.project}/web/gunicorn.socket";
frappe.socketIOSocket = "/run/${cfg.project}/ws/socketIO.socket";
frappe.benchDirectory = "/var/lib/${cfg.project}";
Expand All @@ -251,8 +258,8 @@ in {
frappe.packages = flatten (catAttrs "packages" cfg.apps);
frappe.environment = {
FRAPPE_STREAM_LOGGING = "1";
FRAPPE_REDIS_CACHE = "unix://${cfg.redisCacheSocket}";
FRAPPE_REDIS_QUEUE = "unix://${cfg.redisQueueSocket}";
FRAPPE_REDIS_CACHE = "unix://${cfg.cacheSocket}";
FRAPPE_REDIS_QUEUE = "unix://${cfg.queueSocket}";
FRAPPE_SOCKETIO_UDS = cfg.socketIOSocket;
FRAPPE_DB_SOCKET = cfg.mariadbSocket;
FRAPPE_SITES_ROOT = "${cfg.benchDirectory}/sites";
Expand All @@ -266,24 +273,33 @@ in {
frappe.commonSiteConfig = {workers = cfg.workerQueues;};

# setup redis service
redis = {
vmOverCommit = true;
servers."${cfg.project}-cache" = {
bind = null; # only use unix socket
user = cfg.project;
enable = true;
appendOnly = true;
save = [];
settings = {
maxmemory = "794mb";
maxmemory-policy = "allkeys-lru";
redis.vmOverCommit = true;
redis.servers =
{
"${cfg.project}-queue" = {
bind = null; # only use unix socket
enable = true;
user = cfg.project;
};
}
// optionalAttrs (!cfg.useDragonfly) {
"${cfg.project}-cache" = {
bind = null; # only use unix socket
user = cfg.project;
enable = true;
appendOnly = true;
save = [];
settings = {
maxmemory = "794mb";
maxmemory-policy = "allkeys-lru";
};
};
};
servers."${cfg.project}-queue" = {
bind = null; # only use unix socket
enable = true;
user = cfg.project;
};
# setup dragonfly db service (redis alternative)
dragonflydb = mkIf (cfg.useDragonfly) {
enable = true;
unixSocket = cfg.cacheSocket;
user = cfg.project;
};

# setup mysql service + project user, site databases & site users
Expand Down
5 changes: 5 additions & 0 deletions src/overlays/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@
__functor = _: (import ./tools inputs);
meta.description = "Frappix tools overlays";
};

packages = {
__functor = _: (import ./packages inputs);
meta.description = "Frappix extra packages";
};
}
3 changes: 3 additions & 0 deletions src/overlays/packages/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
inputs: final: prev: {
dragonflydb = final.callPackage ./dragonflydb {};
}
Loading