# Functions

> Load named Lua function libraries with FUNCTION LOAD, call them with FCALL and FCALL_RO, and keep them across restarts.

URL: https://docs.hotkv.com/develop/functions/


Functions are server-side Lua code with names. Instead of sending a script and
tracking its SHA1 digest, you load a library once and then call its functions
by name, like commands. Libraries are part of the data set: they are saved
with snapshots, written to the append-only file, and sent to replicas.

Functions run in the same Lua environment as [scripts](https://docs.hotkv.com/develop/lua-scripting/index.md),
with the same libraries, sandbox rules, atomicity and time limits.

## Writing a library

A library is Lua code whose first line is `#!lua name=<library>`. When it is
loaded, its top-level code runs once and registers its functions with
`redis.register_function()`. Each function receives two tables, the keys and
the other arguments.

```lua
#!lua name=inventory

local function take(keys, args)
  local stock = tonumber(redis.call('GET', keys[1]) or '0')
  local wanted = tonumber(args[1])
  if stock < wanted then
    return redis.error_reply('insufficient stock')
  end
  return redis.call('DECRBY', keys[1], wanted)
end

local function level(keys, args)
  return redis.call('GET', keys[1])
end

redis.register_function('take', take)
redis.register_function{
  function_name = 'level',
  callback = level,
  flags = { 'no-writes' },
  description = 'Current stock for one item',
}
```

The table form of `register_function` takes `function_name`, `callback`, and
optionally `flags` and `description`. Library names may contain letters,
digits and underscores. The function flags are the same as for scripts:
`no-writes`, `allow-oom`, `allow-stale`, `no-cluster` and
`allow-cross-slot-keys` (see
[Lua scripting](https://docs.hotkv.com/develop/lua-scripting/index.md#read-only-scripts-and-flags)).

## Loading and calling

[FUNCTION LOAD](https://docs.hotkv.com/commands/function-load/index.md) takes the library code and replies
with the library name. With `redis-cli`, `-x` reads the last argument from
standard input, which is the easiest way to send a file:

```bash
redis-cli -x FUNCTION LOAD < inventory.lua
```

```
"inventory"
```

Loading a library whose name already exists fails with
`ERR Library 'inventory' already exists`; pass `REPLACE` to update it:

```bash
redis-cli -x FUNCTION LOAD REPLACE < inventory.lua
```

[FCALL](https://docs.hotkv.com/commands/fcall/index.md) calls a function with the number of keys, the keys,
and the arguments, just like `EVAL`. [FCALL_RO](https://docs.hotkv.com/commands/fcall_ro/index.md) calls a
function registered with `no-writes`, and can run on a read-only replica.

```
127.0.0.1:6379> SET stock:widget 10
OK
127.0.0.1:6379> FCALL take 1 stock:widget 3
(integer) 7
127.0.0.1:6379> FCALL take 1 stock:widget 30
(error) insufficient stock
127.0.0.1:6379> FCALL_RO level 1 stock:widget
"7"
127.0.0.1:6379> FCALL missing 0
(error) ERR Function not found
```

> **Tip**
> As with scripts, pass every key a function touches through the key arguments
> of `FCALL`. Cluster routing and ACL key checks depend on them.

## Managing libraries

| Command | What it does |
| --- | --- |
| [FUNCTION LIST](https://docs.hotkv.com/commands/function-list/index.md) `[LIBRARYNAME pattern] [WITHCODE]` | Each library with its engine, functions, descriptions and flags |
| [FUNCTION DELETE](https://docs.hotkv.com/commands/function-delete/index.md) `library` | Remove a library and its functions |
| [FUNCTION FLUSH](https://docs.hotkv.com/commands/function-flush/index.md) `[ASYNC \| SYNC]` | Remove every library |
| [FUNCTION DUMP](https://docs.hotkv.com/commands/function-dump/index.md) | Every library as one payload, for backup |
| [FUNCTION RESTORE](https://docs.hotkv.com/commands/function-restore/index.md) `payload [FLUSH \| APPEND \| REPLACE]` | Load a `FUNCTION DUMP` payload |
| [FUNCTION STATS](https://docs.hotkv.com/commands/function-stats/index.md) | Number of libraries and functions |
| [FUNCTION KILL](https://docs.hotkv.com/commands/function-kill/index.md) | Stop a function that is running too long |

```
127.0.0.1:6379> FUNCTION LIST
1) 1) "library_name"
   2) "inventory"
   3) "engine"
   4) "LUA"
   5) "functions"
   6) 1) 1) "name"
         2) "take"
         3) "description"
         4) (nil)
         5) "flags"
         6) (empty array)
      2) 1) "name"
         2) "level"
         3) "description"
         4) "Current stock for one item"
         5) "flags"
         6) 1) no-writes
```

`FUNCTION RESTORE` appends by default and fails if a library in the payload
already exists. `REPLACE` overwrites libraries of the same name, and `FLUSH`
removes every library first.

> **Caution**
> A `FUNCTION DUMP` payload is in HotKV's own format: restore it into HotKV
> only. To move libraries between HotKV and Redis or Valkey, in either
> direction, load their source code with `FUNCTION LOAD` (`FUNCTION LIST
> WITHCODE` shows it).

## Long-running functions

A function that runs longer than `busy-reply-threshold` (5000 ms by default)
makes other clients' waiting commands reply `BUSY`, as a script does.
`FUNCTION KILL` stops it if it has not written anything yet; otherwise it
replies `UNKILLABLE`. See
[Lua scripting](https://docs.hotkv.com/develop/lua-scripting/index.md#long-running-scripts).

While `FUNCTION LOAD` runs a library's top-level code, that code may only
register functions; it cannot call commands, and it is stopped if it runs for
more than half a second.

## Persistence and replication

- Libraries are saved in snapshots and restored when the server starts.
- `FUNCTION LOAD`, `DELETE`, `FLUSH` and `RESTORE` are written to the
  append-only file and sent to replicas, so replicas always have the
  primary's libraries. A replica refuses them from its own clients.
- In a cluster, each shard has its own libraries. Load a library on every
  primary, for example with `redis-cli --cluster call <host:port> FUNCTION LOAD ...`.
  See [What cannot be clustered](https://docs.hotkv.com/clustering/keys-and-commands/index.md#what-cannot-be-clustered).

## Compatibility

Library syntax, `FCALL`, `FCALL_RO` and the `FUNCTION` subcommands behave as
in Redis 7. The difference is the `FUNCTION DUMP` payload format, which is
specific to HotKV.