---
title: "Developing Record UDFs"
description: "Guide to developing Aerospike Record UDFs to augment read/write behavior and implement atomic single-record operations."
---

# Developing Record UDFs

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

A record UDF is a User-Defined Function (UDF) that is applied to a **single record**. Record UDFs can be used to augment both read and write behavior.

## Typical uses for record-based UDFs

-   Record UDFs can be used to implement an atomic operation that does not currently exist in the client. As a UDF will not perform at the same speed and scale as native operations, it is important to consider whether the rich [List](https://aerospike.com/docs/develop/data-types/collections/list) and [Map](https://aerospike.com/docs/develop/data-types/collections/map) API methods cannot achieve the same effect. Combining multiple native operations through the `operate()` command should also be considered, before using a UDF.
    
-   Record UDFs can be used for single-record, multi-operation transactions where data from one bin is used to determine the value of another bin.
    
-   Background UDFs are good for large scale maintenance and data normalization, with a record UDF applied to all the records returned by a scan or query.
    

## General comments on record UDFs:

-   The first argument of the function, such as `rec`, refers to the database record. Do not name the variable `record` as this is one of the types in the [Aerospike Lua API](https://aerospike.com/docs/database/8.0.0/advanced/udf/api/types/record).
-   Each subsequent argument is specific to the UDF, and must be one of the types supported by the database: numeric (integer or double), string, list or map.
-   A record UDF should have one or more parameters (the record and optionally more). If a UDF has N parameters, and only (N-k) arguments passed in, the last k will automatically be assigned a value of `nil`.
-   A record UDF may return one of the types supported by the database: numeric (integer or double), string, bytes, list or map.

may be applied to record UDF transactions.

-   A background UDF, where a single UDF is applied by [primary index query](https://aerospike.com/docs/develop/learn/queries/primary-index#background-queries) or a [secondary index query](https://aerospike.com/docs/develop/learn/queries/secondary-index), modifies records without returning a result (write only).
-   A record UDF command inside a transaction will lock the record (blocking others from writing to it with an [`AS_ERR_MRT_BLOCKED`](https://aerospike.com/docs/database/reference/error-codes) error code 120) regardless of whether it performs read or write operations.

## Record TTL and UDF

When creating or updating a record, its new TTL value adheres to the following hierarchy:

-   If the UDF explicitly calls [`record.set_ttl()`](https://aerospike.com/docs/database/8.0.0/advanced/udf/api/types/record), that value is used. See [Record TTL and UDF](https://aerospike.com/docs/database/advanced/udf/modules/record/develop/#record-ttl-and-udf).
-   If there is a ttl specified in the policy field of the client UDF call, that value is used. See [Expiration (Time to Live)](https://aerospike.com/docs/database/8.0.0/learn/policies/#expiration-time-to-live).
-   If the client policy passes a TTL of 0, the namespace’s [`default-ttl`](https://aerospike.com/docs/database/reference/config#namespace__default-ttl) will be used.

## Example: Record create or update

In this simple example, [Annotated Record UDF Example](https://aerospike.com/docs/database/8.0.0/advanced/udf/modules/record/examples/annotated), we show how the UDF can create or update an Aerospike record.

## Example: String slice operator

Aerospike has two atomic operations for the [String](https://aerospike.com/docs/develop/data-types/scalar#string) data type - append and prepend. The following record UDF adds an atomic string slice operator.

```lua
function slice(rec, bin, a, b)

  local s = rec[bin]

  if type(s) == 'string' then

    return s:sub(a, b)

  end

  return nil

end
```

In aql

Terminal window

```bash
aql> register module 'util.lua'

OK, 1 module added.

aql> insert into test.foo (PK, x) values ('1', "Alright I like the beat except the snare, kick and keys")

OK, 1 record affected.

aql> execute util.slice('x', 9, 23) on test.foo where PK='1'

+-------------------+

| slice             |

+-------------------+

| "I like the beat" |

+-------------------+

1 row in set (0.001 secs)
```

## Example: Using Protobuf

In this example, a UDF makes use of an external Lua module, which in turns calls a C shared object. The [Protobuf Module Example](https://aerospike.com/docs/database/8.0.0/advanced/udf/modules/record/examples/protobuf) gives more details about the code involved in this example.

```plaintext
aql> select * from test.foo where PK='1'

Error: (2) AEROSPIKE_ERR_RECORD_NOT_FOUND

aql> execute pbuf.vartix(123, 'JDoe', 'j.doe@gmail.com') on test.foo where PK='1'

+--------+

| vartix |

+--------+

|        |

+--------+

1 row in set (0.001 secs)

aql> select * from test.foo where PK='1'

+----------------------------------------------------------------------------------------------+

| person                                                                                       |

+----------------------------------------------------------------------------------------------+

| 12 03 57 69 6C 08 7B 1A 16 77 69 6C 2E 6A 61 6D 69 65 73 6F 6E 40 67 6D 61 69 6C 2E 63 6F 6D |

+----------------------------------------------------------------------------------------------+

1 row in set (0.000 secs)

aql> execute pbuf.velkor() on test.foo where PK='1'

+--------+

| velkor |

+--------+

| 123    |

+--------+

1 row in set (0.000 secs)
```

## Example: Using a background UDF to reset TTLs

The [Background UDF Example](https://aerospike.com/docs/database/8.0.0/advanced/udf/modules/record/examples/background) resets the TTL of records accidentally set to never expire.