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 而从数据库中删除了某个资源的锁(假设其他 service/thread 早些时候获得了它),当再次尝试获取锁时,它仍然会抛出一个状态码为 409 的 CosmosAccessException。 (“违反唯一索引约束”)。对我来说,尽管锁已从数据库中删除(因为我检查过),但它仍然在某处保留了一些关于该锁的剩余信息。
LOCK DTO(我没有添加getters和setters):
@Container(containerName = "distributed-lock", timeToLive = -1, autoCreateContainer = false)
public 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://docs.microsoft.com/bs-latn-ba/azure/cosmos-db/sql/time-to-live?view=sql-server-ver15
我正在使用 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 而从数据库中删除了某个资源的锁(假设其他 service/thread 早些时候获得了它),当再次尝试获取锁时,它仍然会抛出一个状态码为 409 的 CosmosAccessException。 (“违反唯一索引约束”)。对我来说,尽管锁已从数据库中删除(因为我检查过),但它仍然在某处保留了一些关于该锁的剩余信息。
LOCK DTO(我没有添加getters和setters):
@Container(containerName = "distributed-lock", timeToLive = -1, autoCreateContainer = false)
public 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://docs.microsoft.com/bs-latn-ba/azure/cosmos-db/sql/time-to-live?view=sql-server-ver15