Watch cancellation and conflicts caused by watches (?)

I have 2 questions regarding watches:

1.
The Javadoc says “Because a watch outlives the transaction that creates it, any watch that is no longer needed should be cancelled.” How do I cancel a watch in Java when I do not need it anymore?

I got into a situation where a transaction in a concurrent system fails with Transaction not committed due to conflict with another transaction. I read some keys in snapshot isolation and some in normal mode, set some values and then set a watch on a key that I have not read.

The conflicts API shows that the conflicted rows for the transaction contain only the key I set the watch on. Multiple other transactions update the key the watch watches (using set or atomic add) (but they do not read it either).

The only conflict range (single key range I set the watch on):
{conflict=FROM ((235, "main-queue", 27)) TO ((235, "main-queue", 27, null))}

I think not reading a key and setting a watch on it should not cause transaction conflicts in a transaction. Am I missing something? Is there a way to set a watch in a transaction where I modify entries and do not care if the value of the watch changed while the transaction was ongoing?

Edit: I tried 6.3.24 and built 7.1.21, same result.

Simple reproducer:

    @Test
    public void watchCausesConsistencyFailure() {

        var db = FDB.selectAPIVersion(710)
                .open();

        Runnable updater = () -> {
            while (true) {
                db.run(tr -> {
                    tr.mutate(MutationType.ADD, "foo".getBytes(), new byte[]{1, 0, 0, 0, 0, 0, 0, 0});
                    try {
                        Thread.sleep(ThreadLocalRandom.current().nextLong(5, 300));
                    } catch (Exception e) {

                    }
                    return null;
                });
            }
        };

        // Start 5 updater threads
        for(int i = 0; i < 5; i++) {
            new Thread(updater)
                    .start();
        }

        while(true) {
            db.run(tr -> {
                tr.options()
                        .setRetryLimit(0);


                // Modify unrelated key
                tr.set("bar".getBytes(), ("something-"+ThreadLocalRandom.current().nextInt()).getBytes());

                // set a watch
                tr.watch("foo".getBytes());
                try {
                    Thread.sleep(ThreadLocalRandom.current().nextLong(5, 300));
                } catch (Exception e) {

                }
                return null;
            });

            System.out.println("no problem");
        }
    }

Looks like I missed that the CompletableFuture’s cancel() method is fully implemented in NativeFuture, and that can be used, so the first question is clear now :slight_smile: