class EtcdTemplate {
        private final KV kvClient;
        private final Lease leaseClient;

        private EtcdTemplate(Client client) {
            this.kvClient = client.getKVClient();
            this.leaseClient = client.getLeaseClient();
        }

        public Long createLease(long lockUntilInSeconds) {
            try {
                return leaseClient.grant(lockUntilInSeconds).get().getID();
            } catch (Exception e) {
                throw new LockException("Failed create lease", e);
            }
        }

        public Optional<Long> tryToLock(String key, String value, Instant lockAtMostUntil) {
            Long leaseId = createLease(getSecondsUntil(lockAtMostUntil));
            try {
                ByteSequence lockKey = toByteSequence(key);
                PutOption putOption = putOptionWithLeaseId(leaseId);

                // Version is the version of the key.
                // A deletion resets the version to zero and any modification of the key
                // increases its
                // version.
                Txn txn = kvClient.txn()
                        .If(new Cmp(lockKey, Cmp.Op.EQUAL, CmpTarget.version(0)))
                        .Then(Op.put(lockKey, toByteSequence(value), putOption))
                        .Else(Op.get(lockKey, DEFAULT));

                TxnResponse tr = txn.commit().get();
                if (tr.isSucceeded()) {
                    return Optional.of(leaseId);
                } else {
                    revoke(leaseId);
                    return Optional.empty();
                }
            } catch (Exception e) {
                revoke(leaseId);
                throw new LockException("Failed to set lock " + key, e);
            }
        }

        public void revoke(Long leaseId) {<FILL_FUNCTION_BODY>}

        /**
         * Set the provided leaseId lease for the key-value pair similar to the CLI
         * command
         *
         * <p>
         * etcdctl put key value --lease <leaseId>
         *
         * <p>
         * If the key has already been put with an other leaseId earlier, the old
         * leaseId will be timed out and then removed, eventually.
         */
        public void putWithLeaseId(String key, String value, Long leaseId) {
            ByteSequence lockKey = toByteSequence(key);
            ByteSequence lockValue = toByteSequence(value);

            PutOption putOption = putOptionWithLeaseId(leaseId);
            kvClient.put(lockKey, lockValue, putOption);
        }

        private ByteSequence toByteSequence(String key) {
            return ByteSequence.from(key.getBytes(UTF_8));
        }

        private PutOption putOptionWithLeaseId(Long leaseId) {
            return PutOption.newBuilder().withLeaseId(leaseId).build();
        }
    }

            try {
                leaseClient.revoke(leaseId).get();
            } catch (Exception e) {
                throw new LockException("Failed to revoke lease " + leaseId, e);
            }
        
<no_super_class>