CodeVetter · Repo Unpack · Extension points
redis/redis
Redis has four first-class extension mechanisms. New commands are added declaratively: drop a JSON spec in `src/commands/`, implement the proc, and regenerate `src/commands.def`. New configuration parameters are appended to the `static_configs[]` table in `src/config.c` using the `create*Config` macro family. New runtime behaviour is added out-of-process via the module API (`RM_CreateCommand`/`RM_CreateSubcommand` plus data types, filters, and hooks). New scripting languages plug into `functionsRegisterEngine()`. On top of these, this tree adds a fifth: `modules/modules.yaml` as a declarative registry for bundled external modules.
- Command registration is codegen-driven: `src/commands/*.json` is the input, `utils/generate-command-code.py` is the generator, and `src/Makefile`'s rule `$(COMMANDS_DEF_FILENAME).def: commands/*.json ../utils/generate-command-code.py` regenerates `src/commands.def`. CI fails if the checked-in file drifts.
- The generator maps declarative JSON to C enums via `ARG_TYPES` (string/integer/double/key/pattern/unix-time/pure-token/oneof/block) and `GROUPS`, which already contains the newer `COMMAND_GROUP_ARRAY` and `COMMAND_GROUP_RATE_LIMIT` — adding a command group means editing this table.
- The runtime command registry is `redisCommandTable[]` from commands.def, loaded once by `populateCommandTable()` into dicts consulted by `lookupCommandLogic()`, `lookupCommandBySds()` and `lookupCommandByCString()` — module commands are inserted into the same dicts.
- Configuration is a single append-only table: `standardConfig static_configs[]` in src/config.c, populated by 119 `createBoolConfig`/`createStringConfig`/`createEnumConfig`/`createSDSConfig`/etc. calls, each taking a flags mask (`MODIFIABLE_CONFIG`, `IMMUTABLE_CONFIG`, `DEBUG_CONFIG`, `HIDDEN_CONFIG`, `SENSITIVE_CONFIG`) plus optional validate and apply callbacks.
- Config apply-callbacks are the hook for cross-subsystem side effects — e.g. `updateClusterFlags` on `cluster-replica-no-failover`, `updateJemallocBgThread` on `jemalloc-bg-thread`, `updateAppendonly` on `appendonly`, `updateRequirePass` on `requirepass`, and `applyClusterBusPortProtectedMode`.
- The module command API is `RM_CreateCommand(ctx, name, cmdfunc, strflags, firstkey, lastkey, keystep)` with `RM_CreateSubcommand(parent, ...)` for containers — the canonical way third-party code adds commands without touching the core tables.
- Modules can intercept every command before dispatch by registering a command filter, invoked as `moduleCallCommandFilters(c)` at the top of `processCommand()`; `src/module.c` documents a flag to prevent a module's own `RM_Call()` from re-entering its filter.
- Server lifecycle hooks are exposed as module server events — `beforeSleep()` fires `moduleFireServerEvent(REDISMODULE_EVENT_EVENTLOOP, REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP, NULL)` once per loop iteration when `moduleCount()` is non-zero.
- Scripting languages are pluggable through `functionsRegisterEngine(const char *engine_name, engine *engine)`, which stores into a module-level `static dict *engines`; `FUNCTION STATS` reflects the registry back to clients, and the only current registration is `LUA_ENGINE_NAME` from `luaEngineInitEngine` in function_lua.c.
- Keyspace notification is a central hook point via `notifyKeyspaceEvent(type, event, key, dbid)` and the newer `notifyKeyspaceEventWithSubkeys(...)` — new data types must call these to participate in keyspace events and client-side-cache invalidation.
- Connection types are a runtime-registered abstraction: `connTypeInitialize()` in `main()` sets up `server.listeners[j]` for `j < CONN_TYPE_MAX`, with implementations in `src/socket.c`, `src/unix.c` and `src/tls.c` behind `src/connection.h` — TLS can even be built as a loadable module with `BUILD_TLS=module`.
- Bundled external modules are added declaratively by appending an entry to `modules/modules.yaml` with `name`/`repo`/`ref`/`target_module`/`loadmodule` and optional `build_env` — no new Makefile is needed, because `modules/common.mk` derives `MODULE_NAME` from `$(notdir $(CURDIR))` and reads the rest from the manifest.
- All YAML parsing has exactly one implementation: `scripts/lib/manifest.sh`, which is dual-mode — sourced as a shell library (`manifest_modules`, `manifest_field`, `manifest_ref`, `resolve_modules`) and invoked as a CLI by `modules/manifest.mk`. Extending the manifest format means editing that one file.
- New top-level Make goals that take positional module names plug into the `GOALS_WITH_ARGS := <goal>:<VAR>` list in the root Makefile; the dispatch and no-op `.PHONY` targets are generated automatically by the `_capture_goal_args` define.
- New Tcl test suites require no registration — dropping a `.tcl` file into `tests/unit/`, `tests/unit/type/`, `tests/unit/cluster/` or `tests/integration/` is enough, since `test_helper.tcl` globs those directories. The exception is `tests/unit/moduleapi/`, where `runtest-moduleapi` lists each suite explicitly with `--single`.
- The embedded C test harness is extended through the `redisTests[]` table consumed by `getTestProcByName()` in `main()`, letting a data-structure file register a `redisTestProc` runnable as `redis-server test <name>`.