Creating documents, such as counters, that are frequently updated in Fauna is an anti-pattern. Because every update involves creating a history event, this could result in bloated history. Problems arise regardless of history_days setting and/or ttl, as garbage collection is not immediate. If implementing counters or other frequently updated documents is an absolute must, then we recommend the following workarounds:
Updates
Instead of Update(), use this UDF that Updates and removes history at the same time:
CreateFunction({
name: "UpdateAndRemoveHistory",
body: Query(
Lambda(
["ref", "params"],
Let(
{
events: Paginate(Events(Var("ref")), { size: 100000 }),
updated: Update(Var("ref"), Var("params"))
},
Do(
Foreach(
Var("events"),
Lambda(
"event",
Let(
{
ts: Select(["ts"], Var("event")),
action: Select(["action"], Var("event"))
},
Remove(Var("ref"), Var("ts"), Var("action"))
)
)
),
Var("updated")
)
)
)
)
})
Deletes
The same problem with history accumulation can happen with deletes as well. Use the following workaround:
CreateFunction({
name: "DeleteWithHistory",
body: Query(
Lambda(
["ref"],
Let(
{
events: Paginate(Events(Var("ref"))),
after: Select(["after"], Var("events"), null),
result: If(IsNull(Var("after")), Get(Var("ref")), Delete(Var("ref")))
},
Do(
Foreach(
Var("events"),
Lambda(
"event",
Let(
{
ts: Select(["ts"], Var("event")),
action: Select(["action"], Var("event"))
},
Remove(Var("ref"), Var("ts"), Var("action"))
)
)
),
Var("result")
)
)
)
)
})
Caveats
As with everything, there are trade offs when using these approaches. Some important considerations to bare in mind are:
- Each delete using DeleteWithHistory will consume two read ops, while just calling the FQL native Delete() costs zero read ops.
- If you can ensure that the only history for a given Document is the original create event (i.e. the document was created and never updated), then it's better to use Remove() directly.