Thank you.
It is more or less what I thought this was.
I changed my implementation. I wrote an UDF that does the same thing than replace, but also manipultes the history with Insert and Remove, so I have a flag attribute that is always true for most uptodate version, and false for all other historical versions. (It could be done for update to, but I only needed Replace).
With this new flag attribute, I am able to add a term in my index for this flag. When I match this index, I add True for this term. This reduced my read ops dramaticaly (while raising the write ops a bit beacause its needed for Insert and Remove).
Before the fix, for one full day :
48,9 read ops and 2,0 write ops (and the read ops that were raising everyday)
After the fix :
3,5 read ops and 4,3 write ops (and this is more stable).
While preserving full history.
My function if someone wants to do something similar :
def replace_and_update_history(ref, data):
return q.let(
{
"ref": ref,
"last": q.get(q.var("ref")),
"last_ts": q.select("ts", q.var("last"))
},
q.do(
q.replace(q.var("ref"), q.merge(data, {"data": q.merge(q.select("data", data), {"is_last": True}) })),
q.remove(q.var("ref"), q.var("last_ts"), "update"),
q.insert(q.var("ref"), q.var("last_ts"), "update", {"data": q.merge(q.select("data", q.var("last")), {"is_last": False}) }),
)
)
(here it is defined in python but you could adapt this to your language of choice easlily).
Then you use is_last in your index as a term, and match on is_last=True. The history does not cost you anymore on reads (but you have to pay more on writes)