fdb_future_set_callback guarantees at most once. And this can indeed be problematic. Let me try to give some context:
A fdb_future is a C wrapper around a flow future (flow is the actor framework FoundationDB is implemented in). A future can even be set to
Never - in which case it is guaranteed that your callback will never be called. In practice, an explicit
Never should not propagate to the client code (I think we would consider this as a bug otherwise).
So while having exactly once semantic is very desirable it is not guaranteed. And I would argue that it is in general impossible to guarantee exactly once (this would mean that we would need to guarantee that any operation eventually terminates). In fact we had this exact problem in production (we use multiple threads and block on futures and in our case these threads would just block forever - or at least for several days).
As a workaround I would strongly suggest to set the option
transaction_timeout on each transaction that you start. From 6.2 on you can set this on the database level and it will be set for each transaction for you. There is a very strong argument for setting this option by default to something that makes sense (5-10s maybe?) but this makes testing harder (if I remember the discussion correctly - otherwise I believe @ajbeamon might be able to explain this better than I can).
If you set a transaction timeout I think exactly once is guaranteed (unless there are bugs - which of course is possible).