Skip to content

Commit

Permalink
m7l2 concurrency finalization
Browse files Browse the repository at this point in the history
  • Loading branch information
phaeton03 committed Jun 23, 2024
1 parent 319ae87 commit c743333
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal abstract class AdRepoBaseV2Test {
prepareCtx(MkplAdStub.prepareResult {
id = MkplAdId(uuidNew)
ownerId = MkplUserId.NONE
lock = MkplAdLock.NONE
lock = MkplAdLock(uuidNew)
})
.toTransportCreate()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class BizRepoDeleteTest {
ownerId = userId,
adType = MkplDealSide.DEMAND,
visibility = MkplVisibility.VISIBLE_PUBLIC,
lock = MkplAdLock("123-234-abc-ABC"),
)
private val repo = AdRepositoryMock(
invokeReadAd = {
Expand All @@ -50,7 +51,7 @@ class BizRepoDeleteTest {
fun repoDeleteSuccessTest() = runTest {
val adToUpdate = MkplAd(
id = MkplAdId("123"),
lock = MkplAdLock("123"),
lock = MkplAdLock("123-234-abc-ABC"),
)
val ctx = MkplContext(
command = command,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class BizRepoUpdateTest {
description = "xyz",
adType = MkplDealSide.DEMAND,
visibility = MkplVisibility.VISIBLE_TO_GROUP,
lock = MkplAdLock("123"),
lock = MkplAdLock("123-234-abc-ABC"),
)
val ctx = MkplContext(
command = command,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fun repoNotFoundTest(command: MkplCommand) = runTest {
description = "xyz",
adType = MkplDealSide.DEMAND,
visibility = MkplVisibility.VISIBLE_TO_GROUP,
lock = MkplAdLock("123"),
lock = MkplAdLock("123-234-abc-ABC"),
),
)
processor.exec(ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package ru.otus.otuskotlin.marketplace.common.repo

import ru.otus.otuskotlin.marketplace.common.models.MkplAd
import ru.otus.otuskotlin.marketplace.common.models.MkplAdId
import ru.otus.otuskotlin.marketplace.common.models.MkplAdLock

data class DbAdIdRequest(
val id: MkplAdId,
val lock: MkplAdLock = MkplAdLock.NONE,
) {
constructor(ad: MkplAd): this(ad.id)
constructor(ad: MkplAd): this(ad.id, ad.lock)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package ru.otus.otuskotlin.marketplace.common.repo

import ru.otus.otuskotlin.marketplace.common.helpers.errorSystem
import ru.otus.otuskotlin.marketplace.common.models.MkplAd
import ru.otus.otuskotlin.marketplace.common.models.MkplAdId
import ru.otus.otuskotlin.marketplace.common.models.MkplAdLock
import ru.otus.otuskotlin.marketplace.common.models.MkplError
import ru.otus.otuskotlin.marketplace.common.repo.exceptions.RepoConcurrencyException
import ru.otus.otuskotlin.marketplace.common.repo.exceptions.RepoException

const val ERROR_GROUP_REPO = "repo"

Expand All @@ -22,3 +27,38 @@ val errorEmptyId = DbAdResponseErr(
message = "Id must not be null or blank"
)
)

fun errorRepoConcurrency(
oldAd: MkplAd,
expectedLock: MkplAdLock,
exception: Exception = RepoConcurrencyException(
id = oldAd.id,
expectedLock = expectedLock,
actualLock = oldAd.lock,
),
) = DbAdResponseErrWithData(
ad = oldAd,
err = MkplError(
code = "$ERROR_GROUP_REPO-concurrency",
group = ERROR_GROUP_REPO,
field = "lock",
message = "The object with ID ${oldAd.id.asString()} has been changed concurrently by another user or process",
exception = exception,
)
)

fun errorEmptyLock(id: MkplAdId) = DbAdResponseErr(
MkplError(
code = "$ERROR_GROUP_REPO-lock-empty",
group = ERROR_GROUP_REPO,
field = "lock",
message = "Lock for Ad ${id.asString()} is empty that is not admitted"
)
)

fun errorDb(e: RepoException) = DbAdResponseErr(
errorSystem(
violationCode = "dbLockEmpty",
e = e
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class AdRepoInMemory(
val rqAd = rq.ad
val id = rqAd.id.takeIf { it != MkplAdId.NONE } ?: return@tryAdMethod errorEmptyId
val key = id.asString()
val oldLock = rqAd.lock.takeIf { it != MkplAdLock.NONE } ?: return@tryAdMethod errorEmptyLock(id)

mutex.withLock {
val oldAd = cache.get(key)?.toInternal()
Expand All @@ -71,6 +72,7 @@ class AdRepoInMemory(
override suspend fun deleteAd(rq: DbAdIdRequest): IDbAdResponse = tryAdMethod {
val id = rq.id.takeIf { it != MkplAdId.NONE } ?: return@tryAdMethod errorEmptyId
val key = id.asString()
val oldLock = rq.lock.takeIf { it != MkplAdLock.NONE } ?: return@tryAdMethod errorEmptyLock(id)

mutex.withLock {
val oldAd = cache.get(key)?.toInternal()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ package ru.otus.otuskotlin.marketplace.backend.repo.tests
import ru.otus.otuskotlin.marketplace.common.models.*

abstract class BaseInitAds(private val op: String): IInitObjects<MkplAd> {
open val lockOld: MkplAdLock = MkplAdLock("20000000-0000-0000-0000-000000000001")
open val lockBad: MkplAdLock = MkplAdLock("20000000-0000-0000-0000-000000000009")

fun createInitTestModel(
suf: String,
ownerId: MkplUserId = MkplUserId("owner-123"),
adType: MkplDealSide = MkplDealSide.DEMAND,
lock: MkplAdLock = lockOld,
) = MkplAd(
id = MkplAdId("ad-repo-$op-$suf"),
title = "$suf stub",
description = "$suf stub description",
ownerId = ownerId,
visibility = MkplVisibility.VISIBLE_TO_OWNER,
adType = adType,
lock = lock,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,40 @@ import kotlin.test.assertNotNull
abstract class RepoAdDeleteTest {
abstract val repo: IRepoAd
protected open val deleteSucc = initObjects[0]
protected open val deleteConc = initObjects[1]
protected open val notFoundId = MkplAdId("ad-repo-delete-notFound")

@Test
fun deleteSuccess() = runRepoTest {
val result = repo.deleteAd(DbAdIdRequest(deleteSucc.id))
val lockOld = deleteSucc.lock
val result = repo.deleteAd(DbAdIdRequest(deleteSucc.id, lock = lockOld))
assertIs<DbAdResponseOk>(result)
assertEquals(deleteSucc.title, result.data.title)
assertEquals(deleteSucc.description, result.data.description)
}

@Test
fun deleteNotFound() = runRepoTest {
val result = repo.readAd(DbAdIdRequest(notFoundId))
val result = repo.readAd(DbAdIdRequest(notFoundId, lock = lockOld))

assertIs<DbAdResponseErr>(result)
val error = result.errors.find { it.code == "repo-not-found" }
assertNotNull(error)
}

@Test
fun deleteConcurrency() = runRepoTest {
val result = repo.deleteAd(DbAdIdRequest(deleteConc.id, lock = lockBad))

assertIs<DbAdResponseErrWithData>(result)
val error = result.errors.find { it.code == "repo-concurrency" }
assertNotNull(error)
}

companion object : BaseInitAds("delete") {
override val initObjects: List<MkplAd> = listOf(
createInitTestModel("delete"),
createInitTestModel("deleteLock"),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import kotlin.test.assertIs
abstract class RepoAdUpdateTest {
abstract val repo: IRepoAd
protected open val updateSucc = initObjects[0]
protected open val updateConc = initObjects[1]
protected val updateIdNotFound = MkplAdId("ad-repo-update-not-found")
protected val lockBad = MkplAdLock("20000000-0000-0000-0000-000000000009")
protected val lockNew = MkplAdLock("20000000-0000-0000-0000-000000000002")

private val reqUpdateSucc by lazy {
MkplAd(
Expand All @@ -20,6 +23,7 @@ abstract class RepoAdUpdateTest {
ownerId = MkplUserId("owner-123"),
visibility = MkplVisibility.VISIBLE_TO_GROUP,
adType = MkplDealSide.SUPPLY,
lock = initObjects.first().lock,
)
}
private val reqUpdateNotFound = MkplAd(
Expand All @@ -29,7 +33,19 @@ abstract class RepoAdUpdateTest {
ownerId = MkplUserId("owner-123"),
visibility = MkplVisibility.VISIBLE_TO_GROUP,
adType = MkplDealSide.SUPPLY,
lock = initObjects.first().lock,
)
private val reqUpdateConc by lazy {
MkplAd(
id = updateConc.id,
title = "update object not found",
description = "update object not found description",
ownerId = MkplUserId("owner-123"),
visibility = MkplVisibility.VISIBLE_TO_GROUP,
adType = MkplDealSide.SUPPLY,
lock = lockBad,
)
}

@Test
fun updateSuccess() = runRepoTest {
Expand All @@ -39,6 +55,7 @@ abstract class RepoAdUpdateTest {
assertEquals(reqUpdateSucc.title, result.data.title)
assertEquals(reqUpdateSucc.description, result.data.description)
assertEquals(reqUpdateSucc.adType, result.data.adType)
assertEquals(lockNew, result.data.lock)
}

@Test
Expand All @@ -49,9 +66,19 @@ abstract class RepoAdUpdateTest {
assertEquals("id", error?.field)
}

@Test
fun updateConcurrencyError() = runRepoTest {
val result = repo.updateAd(DbAdRequest(reqUpdateConc))
assertIs<DbAdResponseErrWithData>(result)
val error = result.errors.find { it.code == "repo-concurrency" }
assertEquals("lock", error?.field)
assertEquals(updateConc, result.data)
}

companion object : BaseInitAds("update") {
override val initObjects: List<MkplAd> = listOf(
createInitTestModel("update"),
createInitTestModel("updateConc"),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object MkplAdStubBolts {
ownerId = MkplUserId("user-1"),
adType = MkplDealSide.DEMAND,
visibility = MkplVisibility.VISIBLE_PUBLIC,
lock = MkplAdLock("123"),
lock = MkplAdLock("123-234-abc-ABC"),
permissionsClient = mutableSetOf(
MkplAdPermissionClient.READ,
MkplAdPermissionClient.UPDATE,
Expand Down

0 comments on commit c743333

Please sign in to comment.