我正在使用 azure-spring-boot-starter-cosmos 創建一個分布式鎖庫
我的圖書館有兩種方法: public void lockResource(String resourceUniqueIdentifier) 和
public void unlockResource(String resourceUniqueIdentifier)
lockResource() 方法將接收我要鎖定的資源的 resourceUniqueIdentifier,創建一個 Lock() 實體并將其保存在資料庫中。
如果鎖已經存在,(通過將 /appName 設定為磁區鍵,將 /lockedResourceId 設定為 azure 門戶中的唯一鍵), lockRepository.save() 方法將拋出 409 狀態碼的例外(沖突,因為已經有一個物體相同的磁區鍵和唯一鍵)
因此,為了獲得鎖,之前獲得它的人需要呼叫 unlockResource(String resourceUniqueIdentifier) 或者資源上的 ttl 需要過期(我還在 Lock dto 上設定了一個 ttl 欄位并在 azure 門戶中啟用它)
我的邏輯將嘗試使用 while(!isNewLock(lock) && isNotMaximumRetries(retries, resourceUniqueIdentifier)) 獲取鎖。完整代碼如下
Cosmos Config:具有“會話”一致性級別的直接模式
問題:即使由于TTL EXPIRATION(假設其他一些服務/執行緒更早獲得它)而從資料庫中洗掉了某個資源的鎖,當再次嘗試獲取鎖時,它仍然會拋出CosmosAccessException 409狀態代碼。(“唯一索引約束違規”)。對我來說,雖然鎖已從資料庫中洗掉(因為我檢查過),但它仍然有一些關于某處鎖的剩余資訊。
LOCK 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
}
}
鎖定服務:
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;
}
}
uj5u.com熱心網友回復:
問題是當一個資源的離開時間到期時,cosmos 只做部分洗掉。如果有人呼叫該資源,由于 ttl 過期,它不會被回傳。但是,如果您想在短時間內保存相同的資源(在容器上設定了一些約束,例如 uniqueKey 和 partitionKey),您可能會收到 409 狀態代碼,因為當有足夠的 RU 時資料將被完全洗掉(資源單位)可以這樣做。
https://docs.microsoft.com/bs-latn-ba/azure/cosmos-db/sql/time-to-live?view=sql-server-ver15
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/457396.html
下一篇:信號量的多執行緒問題
