class TcpClientTransaction implements ClientTransaction {
        /** Unique client-side transaction id. */
        private final long txUid;

        /** Server-side transaction id. */
        private final int txId;

        /** Client channel. */
        private final ClientChannel clientCh;

        /** Transaction is closed. */
        private volatile boolean closed;

        /**
         * @param id Transaction ID.
         * @param clientCh Client channel.
         */
        private TcpClientTransaction(int id, ClientChannel clientCh) {
            txUid = txCnt.incrementAndGet();
            txId = id;
            this.clientCh = clientCh;
        }

        /** {@inheritDoc} */
        @Override public void commit() {
            Long threadTxUid;

            if (closed || (threadTxUid = threadLocTxUid.get()) == null)
                throw new ClientException("The transaction is already closed");

            if (txUid != threadTxUid)
                throw new ClientException("You can commit transaction only from the thread it was started");

            endTx(true);
        }

        /** {@inheritDoc} */
        @Override public void rollback() {
            endTx(false);
        }

        /** {@inheritDoc} */
        @Override public void close() {
            try {
                endTx(false);
            }
            catch (Exception ignore) {
                // No-op.
            }
        }

        /**
         * @param committed Committed.
         */
        private void endTx(boolean committed) {<FILL_FUNCTION_BODY>}

        /**
         * Tx ID.
         */
        int txId() {
            return txId;
        }

        /**
         * Client channel.
         */
        ClientChannel clientChannel() {
            return clientCh;
        }

        /**
         * Is transaction closed.
         */
        boolean isClosed() {
            return closed;
        }
    }

            try {
                clientCh.service(ClientOperation.TX_END,
                    req -> {
                        req.out().writeInt(txId);
                        req.out().writeBoolean(committed);
                    }, null);
            }
            catch (ClientConnectionException e) {
                throw new ClientException("Transaction context has been lost due to connection errors", e);
            }
            finally {
                txMap.remove(txUid);

                closed = true;

                Long threadTxUid = threadLocTxUid.get();

                if (threadTxUid != null && txUid == threadTxUid)
                    threadLocTxUid.set(null);
            }
        
<no_super_class>