Why Hmily Is the High‑Performance Asynchronous TCC Framework for Distributed Transactions

This article explains Hmily’s core features, the four key techniques that give it high performance—including Disruptor‑based log handling, asynchronous confirm/cancel execution, ThreadLocal caching, and GuavaCache usage—and provides step‑by‑step guidance on integrating the framework with Dubbo, Spring Cloud, or Motan.

Programmer DD
Programmer DD
Programmer DD
Why Hmily Is the High‑Performance Asynchronous TCC Framework for Distributed Transactions

Hmily Framework Features

Seamless integration with Spring and Spring Boot starter.

Seamless integration with Dubbo, Spring Cloud, Motan and other RPC frameworks.

Multiple transaction‑log storage options (Redis, MongoDB, MySQL, etc.).

Various log‑serialization methods (Kryo, Protostuff, Hessian).

Automatic transaction recovery.

Support for dependent‑transaction propagation.

Zero‑intrusion code, simple and flexible configuration.

Why is Hmily so high‑performance?

1. Uses Disruptor for asynchronous log read/write (a lock‑free, GC‑free concurrency framework).

package com.hmily.tcc.core.disruptor.publisher;
import com.hmily.tcc.common.bean.entity.TccTransaction;
import com.hmily.tcc.common.enums.EventTypeEnum;
import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory;
import com.hmily.tcc.core.coordinator.CoordinatorService;
import com.hmily.tcc.core.disruptor.event.HmilyTransactionEvent;
import com.hmily.tcc.core.disruptor.factory.HmilyTransactionEventFactory;
import com.hmily.tcc.core.disruptor.handler.HmilyConsumerDataHandler;
import com.hmily.tcc.core.disruptor.translator.HmilyTransactionEventTranslator;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.IgnoreExceptionHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * event publisher.
 * @author xiaoyu(Myth)
 */
@Component
public class HmilyTransactionEventPublisher implements DisposableBean {
    private Disruptor<HmilyTransactionEvent> disruptor;
    private final CoordinatorService coordinatorService;
    @Autowired
    public HmilyTransactionEventPublisher(final CoordinatorService coordinatorService) {
        this.coordinatorService = coordinatorService;
    }
    /**
     * disruptor start.
     * @param bufferSize this is disruptor buffer size.
     * @param threadSize this is disruptor consumer thread size.
     */
    public void start(final int bufferSize, final int threadSize) {
        disruptor = new Disruptor<>(new HmilyTransactionEventFactory(), bufferSize, r -> {
            AtomicInteger index = new AtomicInteger(1);
            return new Thread(null, r, "disruptor-thread-" + index.getAndIncrement());
        }, ProducerType.MULTI, new BlockingWaitStrategy());
        final Executor executor = new ThreadPoolExecutor(threadSize, threadSize, 0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(),
                HmilyThreadFactory.create("hmily-log-disruptor", false),
                new ThreadPoolExecutor.AbortPolicy());
        HmilyConsumerDataHandler[] consumers = new HmilyConsumerDataHandler[threadSize];
        for (int i = 0; i < threadSize; i++) {
            consumers[i] = new HmilyConsumerDataHandler(executor, coordinatorService);
        }
        disruptor.handleEventsWithWorkerPool(consumers);
        disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler());
        disruptor.start();
    }
    /**
     * publish disruptor event.
     * @param tccTransaction TccTransaction
     * @param type EventTypeEnum
     */
    public void publishEvent(final TccTransaction tccTransaction, final int type) {
        final RingBuffer<HmilyTransactionEvent> ringBuffer = disruptor.getRingBuffer();
        ringBuffer.publishEvent(new HmilyTransactionEventTranslator(type), tccTransaction);
    }
    @Override
    public void destroy() {
        disruptor.shutdown();
    }
}

Buffer size default is 4094 × 4 and can be configured by the user.

2. Asynchronously executes confirm and cancel methods.

package com.hmily.tcc.core.service.handler;
import com.hmily.tcc.common.bean.context.TccTransactionContext;
import com.hmily.tcc.common.bean.entity.TccTransaction;
import com.hmily.tcc.common.enums.TccActionEnum;
import com.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory;
import com.hmily.tcc.core.service.HmilyTransactionHandler;
import com.hmily.tcc.core.service.executor.HmilyTransactionExecutor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * this is transaction starter.
 * @author xiaoyu
 */
@Component
public class StarterHmilyTransactionHandler implements HmilyTransactionHandler {
    private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() << 1;
    private final HmilyTransactionExecutor hmilyTransactionExecutor;
    private final Executor executor = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 0, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(),
            HmilyThreadFactory.create("hmily-execute", false),
            new ThreadPoolExecutor.AbortPolicy());
    @Autowired
    public StarterHmilyTransactionHandler(final HmilyTransactionExecutor hmilyTransactionExecutor) {
        this.hmilyTransactionExecutor = hmilyTransactionExecutor;
    }
    @Override
    public Object handler(final ProceedingJoinPoint point, final TccTransactionContext context) throws Throwable {
        Object returnValue;
        try {
            TccTransaction tccTransaction = hmilyTransactionExecutor.begin(point);
            try {
                returnValue = point.proceed();
                tccTransaction.setStatus(TccActionEnum.TRYING.getCode());
                hmilyTransactionExecutor.updateStatus(tccTransaction);
            } catch (Throwable throwable) {
                final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction();
                executor.execute(() -> hmilyTransactionExecutor.cancel(currentTransaction));
                throw throwable;
            }
            final TccTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction();
            executor.execute(() -> hmilyTransactionExecutor.confirm(currentTransaction));
        } finally {
            hmilyTransactionExecutor.remove();
        }
        return returnValue;
    }
}

If the try‑phase AOP throws an exception, cancel is run asynchronously; otherwise confirm runs asynchronously after the try succeeds.

3. Use of ThreadLocal cache for transaction information.

public TccTransaction begin(final ProceedingJoinPoint point) {
    LogUtil.debug(LOGGER, () -> "......hmily transaction! start....");
    final TccTransaction tccTransaction = buildTccTransaction(point, TccRoleEnum.START.getCode(), null);
    CURRENT.set(tccTransaction);
    hmilyTransactionEventPublisher.publishEvent(tccTransaction, EventTypeEnum.SAVE.getCode());
    TccTransactionContext context = new TccTransactionContext();
    context.setAction(TccActionEnum.TRYING.getCode());
    context.setTransId(tccTransaction.getTransId());
    context.setRole(TccRoleEnum.START.getCode());
    TransactionContextLocal.getInstance().set(context);
    return tccTransaction;
}

ThreadLocal is not used in participants because try and confirm may run in different threads or machines, which would invalidate the ThreadLocal value.

4. Use of GuavaCache.

public final class TccTransactionCacheManager {
    private static final int MAX_COUNT = 10000;
    private static final LoadingCache<String, TccTransaction> LOADING_CACHE = CacheBuilder.newBuilder()
            .maximumWeight(MAX_COUNT)
            .weigher((Weigher<String, TccTransaction>) (key, value) -> getSize())
            .build(new CacheLoader<String, TccTransaction>() {
                @Override
                public TccTransaction load(final String key) {
                    return cacheTccTransaction(key);
                }
            });
    private static CoordinatorService coordinatorService = SpringBeanUtils.getInstance().getBean(CoordinatorService.class);
    private static final TccTransactionCacheManager INSTANCE = new TccTransactionCacheManager();
    private TccTransactionCacheManager() {}
    public static TccTransactionCacheManager getInstance() { return INSTANCE; }
    private static int getSize() { return (int) LOADING_CACHE.size(); }
    private static TccTransaction cacheTccTransaction(final String key) {
        return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(new TccTransaction());
    }
    public void cacheTccTransaction(final TccTransaction tccTransaction) {
        LOADING_CACHE.put(tccTransaction.getTransId(), tccTransaction);
    }
    public TccTransaction getTccTransaction(final String key) {
        try { return LOADING_CACHE.get(key); } catch (ExecutionException e) { return new TccTransaction(); }
    }
    public void removeByKey(final String key) {
        if (StringUtils.isNotEmpty(key)) {
            LOADING_CACHE.invalidate(key);
        }
    }
}

If the cache miss occurs, the framework queries the log to guarantee correct behavior in a clustered environment.

These four points make Hmily an asynchronous, high‑performance distributed TCC transaction framework.

How to use Hmily?

Because of a previous package‑naming issue, the framework is not published to Maven Central; users must clone the repository and deploy the artifacts to a private repository.

1. Dubbo users

Add the following dependency to the API project:

<dependency>
    <groupId>com.hmily.tcc</groupId>
    <artifactId>hmily-tcc-annotation</artifactId>
    <version>{your version}</version>
</dependency>

Add the following dependency to the provider project:

<dependency>
    <groupId>com.hmily.tcc</groupId>
    <artifactId>hmily-tcc-dubbo</artifactId>
    <version>{your version}</version>
</dependency>

Configure the bootstrap bean (e.g., hmilyTransactionBootstrap) with properties such as serializer, recoverDelayTime, retryMax, scheduledDelay, repositorySupport, and database connection details.

2. Spring Cloud users

<dependency>
    <groupId>com.hmily.tcc</groupId>
    <artifactId>hmily-tcc-springcloud</artifactId>
    <version>{your version}</version>
</dependency>

Use the same bootstrap‑bean configuration as for Dubbo.

3. Motan users

<dependency>
    <groupId>com.hmily.tcc</groupId>
    <artifactId>hmily-tcc-motan</artifactId>
    <version>{your version}</version>
</dependency>

Configure the bootstrap bean similarly and add a YAML block such as:

hmily:
  tcc:
    serializer: kryo
    recoverDelayTime: 128
    retryMax: 3
    scheduledDelay: 128
    scheduledThreadMax: 10
    repositorySupport: db
    tccDbConfig:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.1.98:3306/tcc?useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456

After the configuration, annotate service methods with @Tcc to enable distributed transaction management.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavatccHmilyGuavaCache
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.