我正在创建一个分布式锁库,使用蔚蓝-弹簧-启动-启动-宇宙。
我的库有两个方法: public void lockResource(String resourceUniqueIdentifier)和
公共空unlockResource(String resourceUniqueIdentifier)
lockResource()方法将接收我希望锁定的资源的resourceUniqueIdentifier,创建Lock()实例并将其保存在db中。
如果锁已经存在(通过在azure门户中将/appName设置为分区键,将/lockedResourceId设置为唯一键),则lockRepository.save()方法将抛出一个带有409状态代码的异常(因为已经存在一个具有相同分区键和唯一键的实体)。
因此,为了获得锁,先前获得锁的人需要调用unlockResource(String resourceUniqueIdentifier)或资源上的ttl需要过期(我还在lock dto上设置了一个ttl字段,并在蔚蓝门户中启用了它)
我的逻辑将尝试使用while(!isNewLock(锁) && isNotMaximumRetries(retries,resourceUniqueIdentifier))获取锁。下面是完整代码
Cosmos Config:具有“会话”一致性级别的直接模式
问题:即使某个资源的锁由于TTL过期而从数据库中删除(假设前面有其他服务/线程获得了锁),当再次尝试获取锁时,它仍然会抛出一个带有409状态代码的CosmosAccessException。(“唯一索引约束违反”)。对我来说,虽然锁已经从db中删除(因为我检查过了),但它仍然有一些关于该锁的其他信息。
锁DTO (我没有添加getter和setter):
@Container(containerName = "distributed-lock", timeToLive = -1, autoCreateContainer = false)公共类锁{
@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
}}
LockService:
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;
}
}发布于 2022-04-05 10:46:29
问题是,当资源离开的时间到期时,cosmos只执行部分删除。如果有人调用该资源,那么它将不会被返回,因为ttl过期了。但是,如果您想在经过少量的(在容器上设置了一些约束,如uniqueKey和partitionKey)之后保存相同的资源,您可能会收到409状态代码,因为当有足够的资源单元可用时,数据将被完全删除。
https://learn.microsoft.com/bs-latn-ba/azure/cosmos-db/sql/time-to-live?view=sql-server-ver15
https://stackoverflow.com/questions/71738272
复制相似问题