繁体   English   中英

TTL 删除项目后 COSMOS DB 唯一索引约束冲突

[英]COSMOS DB Unique index constraint violation after item was removed by TTL

我正在使用 azure-spring-boot-starter-cosmos 创建分布式锁库

我的图书馆有两种方法: public void lockResource(String resourceUniqueIdentifier) 和
public void unlockResource(String resourceUniqueIdentifier)

lockResource() 方法将接收我要锁定的资源的 resourceUniqueIdentifier,创建一个 Lock() 实例并将其保存在数据库中。

如果锁已经存在,(通过在 azure 门户中将 /appName 设置为分区键并将 /lockedResourceId 设置为唯一键),lockRepository.save() 方法将抛出 409 状态代码的异常(冲突,因为已经有一个实体与相同的分区键和唯一键)

因此,为了获取锁,之前获取锁的人需要调用 unlockResource(String resourceUniqueIdentifier) 或资源上的 ttl 需要过期(我还在 Lock dto 上设置了一个 ttl 字段并在 azure 门户中启用了它)

我的逻辑将尝试使用 while(,isNewLock(lock) && isNotMaximumRetries(retries.resourceUniqueIdentifier)) 获取锁。 完整代码如下

Cosmos Config :具有“会话”一致性级别的直接模式

问题:即使由于 TTL EXPIRATION(假设某些其他服务/线程较早获取了它)而从数据库中删除了某个资源的锁,当尝试再次获取锁时,它仍然会抛出状态为 409 的 CosmosAccessException代码。 (“违反唯一索引约束”)。 对我来说,尽管锁已从数据库中删除(自从我检查过),但它仍然在某处保留了一些关于该锁的剩余信息。

锁定 DTO(我没有添加 getter 和 setter):

@Container(containerName = "distributed-lock", timeToLive = -1, autoCreateContainer = false)

公共 class 锁定 {

@Id
@GeneratedValue
private String id;

private  String lockedResourceId;

@PartitionKey
private  String appName;

private  Integer ttl;

private long time;

public Lock(String id, String lockedResourceId, String appName, Integer ttl, long time) {
    this.id = id;
    this.lockedResourceId = lockedResourceId;
    this.appName = appName;
    this.ttl = ttl;
    this.time = time; //todo remove
}

}

锁定服务:

 public void lockResource(String resourceUniqueIdentifier) {
    var retries = 0;
    var lock = new Lock(UUID.randomUUID().toString(), resourceUniqueIdentifier, appName, lockProperties.getTtl(), Instant.now().toEpochMilli());

    while(!isNewLock(lock) && isNotMaximumRetries(retries, resourceUniqueIdentifier)) {
        logger.info(I9001.getMessage(), lock.getLockedResourceId(), retries);
        waitToUnlock();
        retries++;
    }
}




private void waitToUnlock() {
    try {
        Thread.sleep(lockProperties.getRetryInterval());
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted exception while waiting to retry lock", e);
    }
}



private boolean isNewLock(Lock lockResource) {
    try {
        lockResource.setId(UUID.randomUUID().toString());
        var lock = lockRepository.save(lockResource);
        logger.info(LoggingUtil.X900.getMessage(), lock.getId());
        logger.info("SAVED LOCK WITH ID: {}", lock.getId());
        var sameLock = lockRepository.getByLockedResourceIdAndAppName(lockResource.getLockedResourceId(), lockResource.getAppName());
        logger.info("TESTED SAVED LOCK WITH ID: {}, UniqueId: {}", sameLock.getId(), sameLock.getLockedResourceId());

        return true;
    } catch (CosmosAccessException cosmosAccessException) {
        if (cosmosAccessException.getCosmosException().getStatusCode() == CONFLICT_STATUS_CODE) {
            lockResource.setTime(Instant.now().toEpochMilli());
            logger.info(LoggingUtil.X900.getMessage(), lockResource.getLockedResourceId(), lockResource.getId());
            var alreadyExisting = lockRepository.getByLockedResourceIdAndAppName(lockResource.getLockedResourceId(), lockResource.getAppName());
            logger.info("Retrieved duplicate with resId {} and id {}", alreadyExisting, alreadyExisting);

            return false;
        }
       else throw cosmosAccessException;
    }
}

问题在于,当资源的离开时间到期时,cosmos 只会进行部分删除 如果有人调用该资源,则不会返回,因为 ttl 已过期。 但是如果你想在一小段时间后保存相同的资源(在容器上设置了一些约束,例如 uniqueKey 和 partitionKey)你可能会收到 409 状态代码,因为当有足够的 RU 时数据将被完全删除(资源单位)可用于这样做。

https://learn.microsoft.com/bs-latn-ba/azure/cosmos-db/sql/time-to-live?view=sql-server-ver15

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM