Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dd8540c
fix: functionality without suspend
RuReVange Dec 7, 2025
58ca47c
fix: correct retry after logic, semaphore release
RuReVange Dec 7, 2025
ffccdc3
fix: logging duplicate
RuReVange Dec 7, 2025
d68f49c
feat: retry with new timeout for request
RuReVange Dec 7, 2025
d9a15ca
fix: softer rate limiter
RuReVange Dec 7, 2025
060407b
fix: additional retry after
RuReVange Dec 7, 2025
c3eaad4
fix: minRequiredTime
RuReVange Dec 7, 2025
586ff6c
feat: sheduler for retry
RuReVange Dec 7, 2025
92ef404
-- draft: Test async logic in payment external service.
21092004Goda Dec 7, 2025
873d83e
-- draft: Test async logic in payment external service.
21092004Goda Dec 7, 2025
c876969
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
3a4c8e9
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
9bd1887
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
7f6250f
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
47d7c7a
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
ccd9f04
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
d96a185
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
9c1a210
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
df2df29
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
2a3c72e
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
824c90e
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
765e50f
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
3a925b2
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
c5041fd
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
e139c49
Merge remote-tracking branch 'origin/hw-9-io-kuro' into hw-9-io-kuro
21092004Goda Dec 7, 2025
c88ad74
Merge pull request #22 from ByBinPy/hw-9-io-kuro
ByBinPy Dec 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/main/kotlin/ru/quipy/apigateway/APIController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ru.quipy.orders.repository.OrderRepository
import ru.quipy.payments.logic.OrderPayer
import java.time.Duration
import java.util.*
import kotlin.random.Random

@RestController
class APIController(
Expand All @@ -23,7 +24,7 @@ class APIController(
val logger: Logger = LoggerFactory.getLogger(APIController::class.java)

@PostMapping("/users")
suspend fun createUser(@RequestBody req: CreateUserRequest): User {
fun createUser(@RequestBody req: CreateUserRequest): User {
return User(UUID.randomUUID(), req.name)
}

Expand All @@ -32,7 +33,7 @@ class APIController(
data class User(val id: UUID, val name: String)

@PostMapping("/orders")
suspend fun createOrder(@RequestParam userId: UUID, @RequestParam price: Int): Order {
fun createOrder(@RequestParam userId: UUID, @RequestParam price: Int): Order {
val order = Order(
UUID.randomUUID(),
userId,
Expand All @@ -58,9 +59,10 @@ class APIController(
}

@PostMapping("/orders/{orderId}/payment")
suspend fun payOrder(@PathVariable orderId: UUID, @RequestParam deadline: Long): PaymentSubmissionDto {
fun payOrder(@PathVariable orderId: UUID, @RequestParam deadline: Long): PaymentSubmissionDto {
if (!rateLimiter.tick()) {
throw TooManyRequestsException(deadline)
val retryAfterMs = 10L + Random.nextLong(10)
throw TooManyRequestsException(retryAfterMs)
}
val paymentId = UUID.randomUUID()
val order = orderRepository.findById(orderId)?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ class GlobalExceptionHandler(
fun handleTooManyRequests(exception: TooLongRequestException): ResponseEntity<String> {
return ResponseEntity.status(200).body("your request very long, i am so sorry")
}

@ExceptionHandler(TooManyRequestsException::class)
fun handleTooManyRequestsRetriable(exception: TooManyRequestsException): ResponseEntity<String> {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).header("Retry-After", "1").body("to many requests")
val retryAfterSeconds = (exception.retryAfterMs / 1000.0).coerceAtLeast(0.1)
return ResponseEntity
.status(HttpStatus.TOO_MANY_REQUESTS)
.header("Retry-After", retryAfterSeconds.toString())
.body("too many requests")
}
}
18 changes: 12 additions & 6 deletions src/main/kotlin/ru/quipy/common/utils/SlidingWindowRateLimiter.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ru.quipy.common.utils

import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.util.Deadline
import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.util.Timeout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -33,18 +31,26 @@ class SlidingWindowRateLimiter(
}
}

fun tickBlocking() {
fun tickBlockingAsync() {
while (!tick()) {
Thread.sleep(10)
}
}

suspend fun tickBlocking(timeout: Long): Boolean {
fun tickBlockingWithTimeout(timeout: Long): Boolean {
val timeStarted = System.currentTimeMillis()
while (System.currentTimeMillis()-timeStarted < timeout && !tick()) {
while (System.currentTimeMillis() - timeStarted < timeout && !tick()) {
Thread.sleep(2)
}
return System.currentTimeMillis() - timeStarted < timeout
}

suspend fun tickBlockingAsync(timeout: Long): Boolean {
val timeStarted = System.currentTimeMillis()
while (System.currentTimeMillis() - timeStarted < timeout && !tick()) {
delay(2)
}
return System.currentTimeMillis()-timeStarted < timeout
return System.currentTimeMillis() - timeStarted < timeout
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package ru.quipy.exceptions

class TooManyRequestsException(val deadline: Long) : RuntimeException()
class TooManyRequestsException(val retryAfterMs: Long) : RuntimeException()
2 changes: 1 addition & 1 deletion src/main/kotlin/ru/quipy/payments/logic/OrderPayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class OrderPayer(meterRegistry: MeterRegistry) {
private lateinit var paymentService: PaymentService


suspend fun processPayment(orderId: UUID, amount: Int, paymentId: UUID, deadline: Long): Long {
fun processPayment(orderId: UUID, amount: Int, paymentId: UUID, deadline: Long): Long {
val createdAt = System.currentTimeMillis()
plannedRequests.increment()
val createdEvent = paymentESService.create {
Expand Down
Loading