[英]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.