Implementing Distributed Transactions and Media Asset Management

Distributed Transactions in Microservices

In microservice architectures, a single business operation often spans multiple services and databases. Unlike local transactions that follow the ACID (Atomicity, Consistency, Isolation, Durability) principles within a single database, distributed transactions must ensure data consistency across networked nodes. When one service succeeds but a subsequent service fails, the system must either roll back all changes or compensate for the inconsistency.

CAP Theorem and BASE Theory

Design choices in distributed systems are guided by the CAP Theorem, which states that a system can only prioritize two of the following three attributes:

  • Consistency (C): Every read receives the most recent write or an error.
  • Availability (A): Every request receives a non-error response without the guarantee that it contains the most recent write.
  • Partition Tolerance (P): The system continues to operate despite an arbitrary number of messages being dropped or delayed by the network.

Since network partitions are inevitable in distributed systems, architects usually choose between CP (Consistency and Partition Tolerance) or AP (Availability and Partition Tolerance).

BASE Theory serves as a pragmatic alternative to ACID for AP systems:

  • Basically Available: The system guarantees availability but may have latency or degraded functionality during failures.
  • Soft State: Data might be in a state of flux; consistency is not enforced at all times.
  • Eventually Consistent: The system will eventually become consistent once all updates have propagated.

Distributed Transaction Solutions

  1. XA Protocol (Two-Phase Commit): A blocking protocol where a coordinator asks participants to "prepare" and then "commit." It ensures high consistency but limits performance.
  2. TCC (Try-Confirm-Cancel): An application-level compensation pattern. The "Try" phase reserves resources, "Confirm" executes the operation, and "Cancel" releases resources if something fails.
  3. Reliable Messaging: Uses Message Queues (MQ) to ensure that downstream services eventually process the data.

Seata Framework Integration

Seata is a high-performance distributed transaction solution providing several modes, with AT (Automatic Transaction) mode being the most common. It uses three components:

  • Transaction Coordinator (TC): Maintains the status of global transactions.
  • Transaction Manager (TM): Defines the scope of the global transaction.
  • Resource Manager (RM): Manages branch transactions and interacts with the TC to report status.

Configuration for Seata

To integrate Seata into a Spring Cloud environment, define a data source proxy to intercept SQL operations and manage the undo_log for rollbacks.

@Configuration
public class TransactionProxyConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource primaryDataSource() {
        return new DruidDataSource();
    }

    @Primary
    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(DataSourceProxy proxy) throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(proxy);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/*.xml"));
        return factoryBean;
    }
}

Enable global transactions by adding the @GlobalTransactional annotation to the service method that initiates the cross-service call.

@Service
public class MediaApprovalServiceImpl implements MediaApprovalService {
    @GlobalTransactional(name = "media-approval-tx", rollbackFor = Exception.class)
    public void approveMedia(Integer assetId) {
        // Update local asset status
        assetMapper.updateStatus(assetId, "APPROVED");
        // Invoke remote user service
        userServiceClient.incrementCredit(userId, 10);
        // If an exception occurs here, both operations roll back
    }
}

Distributed File Storage with FastDFS

FastDFS is a lightweight distributed file system designed for high-capacity storage. It consists of two parts:

  • Tracker Server: Handles load balancing and scheduling.
  • Storage Server: Manages file storage and synchronization.

Java Client Integration

Define a utility to handle file uploads and deletions via the FastDFS client:

@Component
public class FileStorageManager {
    @Autowired
    private FastFileStorageClient fdfsClient;

    public String uploadResource(MultipartFile file) throws IOException {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        StorePath path = fdfsClient.uploadFile(file.getInputStream(), file.getSize(), extension, null);
        return path.getFullPath();
    }

    public void removeResource(String fileUrl) {
        fdfsClient.deleteFile(fileUrl);
    }
}

Media Asset Management Implementation

Managing media materials requires tracking user ownership and file metadata. A ThreadLocal pattern is used to pass user context from the API Gateway to the service layer.

User Context via Filter

public class UserContextFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        String identity = request.getHeader("userId");
        if (identity != null) {
            UserContextHolder.set(Integer.valueOf(identity));
        }
        chain.doFilter(req, res);
    }
}

Upload Logic

When a user uploads a file, the system stores the file in FastDFS and records the metadata in the database.

@Service
public class MaterialServiceImpl implements MaterialService {
    @Autowired
    private FileStorageManager storageManager;
    
    @Override
    public ResponseResult saveMaterial(MultipartFile file) {
        try {
            String filePath = storageManager.uploadResource(file);
            MediaMaterial material = new MediaMaterial();
            material.setUserId(UserContextHolder.get());
            material.setUrl(filePath);
            material.setCreatedTime(new LocalDateTime());
            this.save(material);
            return ResponseResult.ok(material);
        } catch (IOException e) {
            return ResponseResult.error("Upload failed");
        }
    }
}

Material Deletion and Verification

Before deleting a resource from FastDFS, the system must verify if the material is currently referenced by any active content to prevent broken links.

@Override
public ResponseResult deleteMaterial(Integer id) {
    MediaMaterial material = this.getById(id);
    // Check for references in the content-material mapping table
    Integer usageCount = referenceMapper.selectCount(new QueryWrapper<Reference>()
            .eq("material_id", id));
    if (usageCount > 0) {
        return ResponseResult.error("Material is currently in use");
    }
    
    storageManager.removeResource(material.getUrl());
    this.removeById(id);
    return ResponseResult.ok();
}

Tags: Distributed Transactions Seata FastDFS microservices java

Posted on Fri, 08 May 2026 02:00:09 +0000 by imran.rajani