Conversation
WalkthroughAdds a persistent webhook queue: Room DB schema v17 snapshot, new webhook_queue entity, DAO, converters and status enum, repository with retry/backoff and recovery, enqueue worker and a coroutine processor worker, DI bindings, WebHooksService integration (logging + queuing), and localized string additions. Changes
Sequence Diagram(s)sequenceDiagram
participant WS as WebHooksService
participant SW as SendWebhookWorker
participant WQR as WebhookQueueRepository
participant DB as Room DB
participant WQP as WebhookQueueProcessorWorker
participant HTTP as HTTP Client
participant LOG as LogsService
WS->>SW: emit(event) per-webhook (with Context)
SW->>WQR: enqueueWebhook(payload)
WQR->>DB: INSERT INTO webhook_queue(...)
DB-->>WQR: id
SW->>WQP: start/trigger processor
WQP->>WQR: recoverStuckProcessingWebhooks()
WQP->>WQR: getPendingWebhooks(now, limit)
WQR->>DB: SELECT pending WHERE next_attempt <= now
DB-->>WQR: list
loop per webhook
WQP->>WQR: markAsProcessing(id)
WQP->>HTTP: POST payload -> url
alt 2xx
HTTP-->>WQP: success
WQP->>WQR: markAsCompleted(id)
WQP->>LOG: log success
else non-2xx / error
HTTP-->>WQP: error
WQP->>WQR: markAsFailed(id, nextAttempt, error)
WQP->>LOG: log failure
end
end
WQP->>WQR: cleanupOldEntries(cutoff)
WQR->>DB: DELETE old rows
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (6)
app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json (1)
341-344: Consider adding an index onwebhook_idif individual webhook queries are expected.There's no index on
webhook_idalone. If you anticipate queries that filter or group bywebhook_id(e.g., getting all pending items for a specific webhook), an additional index could improve performance. However, if such queries are not planned, the current indices are sufficient.Also applies to: 391-412
app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (1)
153-168: Moveget<Context>()outside the loop to avoid redundant DI lookups.
get<Context>()is called on every iteration. Since the context doesn't change, retrieve it once before the loop.+ val context = get<Context>() + webhooksToProcess.forEach { webhook -> // ... existing skip logic ... try { // Create the webhook event DTO val webhookEventDTO = WebHookEventDTO( id = NanoIdUtils.randomNanoId(), webhookId = webhook.id, event = event, deviceId = deviceId, payload = payload, ) SendWebhookWorker.start( - context = get<Context>(), + context = context, url = webhook.url, data = webhookEventDTO, internetRequired = webhooksSettings.internetRequired )app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt (1)
24-27: Consider usingentriesinstead ofvalues()for future Kotlin compatibility.
values()works butentriesis the recommended approach in modern Kotlin.companion object { fun fromValue(value: String): WebhookStatus? { - return values().find { it.value == value } + return entries.find { it.value == value } }app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt (1)
36-40: Consider addingdefaultValuetocreatedAtandnextAttemptfor schema consistency.These columns have Kotlin defaults but lack Room
defaultValueannotations. While Kotlin defaults work for normal inserts via the DAO, adding schema defaults ensures consistency if raw SQL or migrations ever insert rows directly.- @ColumnInfo(name = "created_at") + @ColumnInfo(name = "created_at", defaultValue = "0") val createdAt: Long = System.currentTimeMillis(), - @ColumnInfo(name = "next_attempt") + @ColumnInfo(name = "next_attempt", defaultValue = "0") val nextAttempt: Long = System.currentTimeMillis(),Note: SQLite doesn't support
CURRENT_TIMESTAMPfor INTEGER columns directly, so0is a placeholder; the actual default will still come from Kotlin.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt (1)
32-43: Inconsistent string quoting in SQL queries.The query uses double quotes (
"pending","failed") while other queries likescheduledWebhooksCountuse single quotes ('pending','failed'). While SQLite accepts both, single quotes are SQL standard for string literals.@Query( """ SELECT * FROM webhook_queue - WHERE status IN ("pending", "failed") AND next_attempt <= :currentTime + WHERE status IN ('pending', 'failed') AND next_attempt <= :currentTime ORDER BY next_attempt ASC LIMIT :limit """ )Apply similar changes to other queries using double quotes (lines 51, 65, 84, 98, 130, 142).
app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt (1)
111-118: Consider adding a maximum cap to prevent excessive backoff delays.With configurable
maxRetries, high retry counts could produce impractical delays (e.g., retryCount=20 → ~30 days). Consider capping the delay:private fun calculateBackoffDelay(retryCount: Int, baseDelayMs: Long): Long { // Exponential backoff: baseDelay * 2^(retryCount - 1) - val multiplier = (1L shl (retryCount - 1)) - return baseDelayMs * multiplier + val multiplier = (1L shl minOf(retryCount - 1, 10)) // Cap at 2^10 = 1024x + return minOf(baseDelayMs * multiplier, MAX_BACKOFF_DELAY_MS) } + + companion object { + private const val MAX_BACKOFF_DELAY_MS = 3600_000L // 1 hour max + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json(1 hunks)app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt(3 hunks)app/src/main/java/me/capcom/smsgateway/data/Module.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/Module.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookConverters.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt(1 hunks)app/src/main/res/values-ru/strings.xml(6 hunks)app/src/main/res/values-zh/strings.xml(1 hunks)app/src/main/res/values/strings.xml(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/TemporaryStorage.kt (1)
get(13-15)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
getForegroundInfo(167-169)app/src/main/java/me/capcom/smsgateway/extensions/GsonBuilder.kt (1)
configure(7-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build release / build-apk
- GitHub Check: Build insecure / build-apk
🔇 Additional comments (26)
app/src/main/res/values-zh/strings.xml (1)
88-88: Localization addition looks good.The Chinese translation for the webhook queue processing status is consistent with the English and Russian variants. String key matches across all locales.
app/src/main/res/values/strings.xml (1)
85-85: English string addition is appropriate.The
processing_webhook_queuestring follows the pattern of similar status messages and integrates well with the existing localization structure.app/src/main/res/values-ru/strings.xml (2)
94-94: Russian webhook queue string is consistent.The Russian translation matches the localization pattern across all three language variants. String key is identical to English and Chinese versions.
3-4: Line-wrapping changes are formatting-only.The long string resources have been reformatted to wrap across multiple lines for improved XML readability. These changes do not alter resource values or behavior—purely stylistic. This is consistent with the AI summary noting no semantic changes.
Also applies to: 9-10, 16-19, 45-46, 66-67, 72-73, 82-83, 98-99, 118-119, 120-121, 122-123, 125-126, 127-128
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (2)
249-291: Good fallback mechanism for resilience.The fallback to direct
WorkManagerprocessing when queue enqueue fails is a sensible defensive pattern that ensures webhooks are still delivered even if the persistent queue has issues.
209-210: LGTM!The shared
Gsoninstance in the companion object is efficient and ensures consistent serialization configuration.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookConverters.kt (1)
14-17: LGTM! Consider logging unknown status values.The fallback to
PENDINGis sensible for data integrity. Optionally, you could log a warning when an unknown value is encountered to aid debugging schema migration issues, but this is not critical.app/src/main/java/me/capcom/smsgateway/data/Module.kt (1)
9-9: LGTM!The DI binding for
webhookQueueDaofollows the established pattern for other DAOs in this module.app/src/main/java/me/capcom/smsgateway/modules/webhooks/Module.kt (1)
3-3: LGTM!The
WebhookQueueRepositoryis properly registered as a singleton using the same pattern asWebHooksService.Also applies to: 9-9
app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json (1)
330-414: Well-structured schema for the webhook queue.The
webhook_queuetable design with composite indices on(status, next_attempt)and(status, created_at)is well-suited for queue processing queries.app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt (1)
17-19: LGTM! Database schema changes are well-structured.The new webhook queue entity is properly integrated: entity registered, version bumped, AutoMigration added, TypeConverters registered, and DAO accessor follows the established pattern.
Also applies to: 28-28, 31-31, 48-48, 51-51, 55-55
app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (1)
126-151: Well-structured webhook emission logic with proper guard clauses.The refactored
emit()method cleanly handles:
- Disabled source filtering
- Missing deviceId fallback with skip handling
- Per-webhook error isolation with try-catch
- Comprehensive logging for debugging
app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt (1)
8-38: Well-designed status enum with clear terminal/processing semantics.The status lifecycle is well-defined:
FAILEDallows retries (non-terminal)PERMANENTLY_FAILEDandCOMPLETEDare terminal states- Helper methods provide clean abstractions for status checks
app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt (1)
12-28: LGTM! Entity structure and indices are well-designed.The composite indices on
(status, next_attempt)and(status, created_at)align perfectly with the DAO query patterns for fetching pending webhooks and cleanup operations.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt (2)
59-105: Well-designed state transition methods.The DAO properly handles all webhook lifecycle states:
markAsFailedatomically increments retry count and schedules next attemptmarkAsPermanentlyFailedcaptures the final error for debugging- Clear separation between recoverable (
failed) and terminal (permanently_failed) states
151-161: LGTM! Statistics data class is well-structured.The aggregate query and corresponding data class provide useful observability into queue health.
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (5)
52-61: LGTM!The dependency injection setup using Koin is clean and appropriate for this worker.
62-108: LGTM!The companion object provides clean start/stop APIs with appropriate backoff configuration and network constraints.
110-172: LGTM!The main processing loop correctly uses
NonCancellableto ensure webhook processing completes even during cancellation, with proper logging and cleanup.
373-398: LGTM!The failure handling with fallback to permanent failure ensures webhooks don't get stuck if retry scheduling fails.
400-409: LGTM!The foreground info implementation follows the same pattern as
SendWebhookWorker.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt (5)
10-26: LGTM!Clean implementation for enqueuing webhooks with appropriate defaults.
28-40: LGTM!Simple delegations to the DAO with appropriate defaults.
56-83: LGTM!The retry logic correctly implements backoff scheduling and falls back to permanent failure when max retries exceeded. The caller handles exceptions appropriately.
120-133: LGTM!Clean extension functions that encapsulate the retry and processing state logic.
95-109: LGTM!Reasonable defaults for cleanup (7 days retention) and stuck webhook recovery (5 minutes timeout).
🤖 Pull request artifacts
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
359-361: Critical:client.close()in finally closes wrong HttpClient instance (duplicate issue).This issue was flagged in previous reviews but remains unresolved. The
clientproperty is a getter that creates a newHttpClienton each access. Line 293'sclient.post(url)creates HttpClient #1, while line 360'sclient.close()creates HttpClient #2 and closes it. HttpClient #1 leaks.Remove
client.close()from the finally block:- } finally { - client.close() - }The client lifecycle should be managed at the batch or worker level after fixing the client initialization (see separate comment on lines 405-423).
405-423: Critical: Client getter creates new HttpClient on every access, causing resource leaks (duplicate issue).This issue was flagged in previous reviews but remains unresolved. The
clientproperty is a getter (get() = ...) that instantiates a newHttpClientevery time it's accessed. Each unclosed client leaks connections, threads, and memory.Use lazy initialization to create a single, reusable client:
- private val client - get() = HttpClient(OkHttp) { + private val client by lazy { + HttpClient(OkHttp) { install(HttpTimeout) { requestTimeoutMillis = PROCESSING_TIMEOUT_MS connectTimeoutMillis = 5000 socketTimeoutMillis = 5000 } install(ContentNegotiation) { gson { configure() } } install(DefaultRequest) { userAgent("${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME}") } install(PayloadSingingPlugin) { secretKeyProvider = { settings.signingKey } } } + }Then close the client once at the end of
doWork()before returning success, or rely on worker termination to clean up.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
getForegroundInfo(167-169)app/src/main/java/me/capcom/smsgateway/extensions/GsonBuilder.kt (1)
configure(7-9)
🔇 Additional comments (4)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (4)
47-108: LGTM: Clean worker setup with flexible start/stop API.The class structure, dependency injection, and companion object API are well-designed. The REPLACE policy ensures only one processor runs at a time, and the optional network constraint provides flexibility.
174-281: LGTM: Robust batch processing with per-webhook error isolation.The queue processing logic is well-structured with stuck webhook recovery, batch processing, and per-webhook error handling that prevents cascading failures. Good observability through detailed logging.
364-400: LGTM: Proper retry fallback and foreground notification setup.The webhook failure handling correctly attempts retry scheduling with a fallback to permanent failure. The foreground notification setup follows standard patterns.
425-425: LGTM: Gson instance correctly initialized once.The gson instance is created as a single instance and reused across all webhook sends, which is the correct approach.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
368-370: Duplicate:client.close()in finally block is problematic.As noted in past review comments, closing the client in the finally block is incorrect. Due to the
clientproperty being a getter (see lines 414-432), this creates a new HttpClient instance and closes it, leaving the actual client used on line 302 unclosed. Once the client property is fixed to use lazy initialization, this close call should be removed from here and moved to the end of batch processing or worker lifecycle.
414-432: Duplicate: Critical resource leak—clientgetter creates new instances on each access.As detailed in past review comments, the
clientproperty is a getter that instantiates a newHttpClientevery time it's accessed. On line 302,client.post(url)creates HttpClient #1, and on line 369,client.close()creates HttpClient #2 and closes it, leaving HttpClient #1 open and leaking connections/threads.The fix is to use lazy initialization:
private val client by lazy { HttpClient(OkHttp) { install(HttpTimeout) { requestTimeoutMillis = PROCESSING_TIMEOUT_MS connectTimeoutMillis = 5000 socketTimeoutMillis = 5000 } install(ContentNegotiation) { gson { configure() } } install(DefaultRequest) { userAgent("${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME}") } install(PayloadSingingPlugin) { secretKeyProvider = { settings.signingKey } } } }Then remove the
client.close()from the finally block insendWebhook()and instead close the client once at the end ofdoWork()or when the worker completes.
🧹 Nitpick comments (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
148-151: Optional: The fixed delay when idle may lead to unnecessary database polling.When
processedCount == 0, the worker delays 5 seconds then immediately checkshasScheduledWebhooks()again (which queries the database). If the queue remains empty for extended periods, this results in repeated DB queries every 5 seconds. Consider exiting the loop when the queue is empty and relying on external triggers (e.g., enqueuing new webhooks) to restart the worker.
334-355: Combine redundant exception handlers.Both
IllegalArgumentExceptionandJSONExceptionhandlers perform identical actions (log error, return false). They can be combined for brevity.Apply this diff:
- } catch (e: IllegalArgumentException) { + } catch (e: Exception) { + when (e) { + is IllegalArgumentException, is JSONException -> { + logsSvc.insert( + priority = LogEntry.Priority.ERROR, + module = NAME, + message = "Webhook validation error: ${e.message}", + context = mapOf( + "webhookId" to webhook.id, + "error" to e.message + ) + ) + false + } + else -> throw e + } + } catch (e: Throwable) { logsSvc.insert( - priority = LogEntry.Priority.ERROR, + priority = LogEntry.Priority.WARN, module = NAME, - message = "Webhook invalid argument: ${e.message}", + message = "Webhook network error: ${e.message}", context = mapOf( "webhookId" to webhook.id, - "error" to e.message + "error" to e.message, + "retryCount" to webhook.retryCount ) ) - false - } catch (e: JSONException) { - logsSvc.insert( - priority = LogEntry.Priority.ERROR, - module = NAME, - message = "Webhook JSON error: ${e.message}", - context = mapOf( - "webhookId" to webhook.id, - "error" to e.message - ) - ) - false - } catch (e: Throwable) { - logsSvc.insert( - priority = LogEntry.Priority.WARN, - module = NAME, - message = "Webhook network error: ${e.message}", - context = mapOf( - "webhookId" to webhook.id, - "error" to e.message, - "retryCount" to webhook.retryCount - ) - ) - false // Network errors should trigger retry + false + }Or more simply:
- } catch (e: IllegalArgumentException) { - logsSvc.insert( - priority = LogEntry.Priority.ERROR, - module = NAME, - message = "Webhook invalid argument: ${e.message}", - context = mapOf( - "webhookId" to webhook.id, - "error" to e.message - ) - ) - false - } catch (e: JSONException) { + } catch (e: Exception) { + when (e) { + is IllegalArgumentException, is JSONException -> { logsSvc.insert( priority = LogEntry.Priority.ERROR, module = NAME, - message = "Webhook JSON error: ${e.message}", + message = "Webhook validation error: ${e.message}", context = mapOf( "webhookId" to webhook.id, "error" to e.message ) ) false - } catch (e: Throwable) { + } + else -> { logsSvc.insert( priority = LogEntry.Priority.WARN, module = NAME, message = "Webhook network error: ${e.message}", context = mapOf( "webhookId" to webhook.id, "error" to e.message, "retryCount" to webhook.retryCount ) ) - false // Network errors should trigger retry + false + } + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
getForegroundInfo(167-169)app/src/main/java/me/capcom/smsgateway/extensions/GsonBuilder.kt (1)
configure(7-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build release / build-apk
- GitHub Check: Build insecure / build-apk
🔇 Additional comments (5)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (5)
186-290: LGTM: Batch processing logic is well-structured.The queue recovery, batch retrieval, and per-webhook processing flow are clearly implemented with appropriate error handling and observability.
376-398: LGTM: Retry handling with appropriate fallback.The method correctly schedules retries with exponential backoff and falls back to marking the webhook as permanently failed if retry scheduling itself fails.
400-409: LGTM: Standard foreground service notification implementation.
434-434: LGTM: Gson properly initialized as a singleton instance.
1-60: LGTM: Clean class structure with proper dependency injection.The imports are appropriate, and the class correctly extends
CoroutineWorkerwith Koin-based dependency injection for services and repositories.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
225-230:runBlockingblocks the calling thread.
start()is called from coroutine scopes inEventsReceiver, andrunBlockingblocks the dispatcher thread. Consider makingstart()a suspend function or using a different approach.Options to address this:
- Make
start()a suspend function and update callers- Use
CoroutineScope(Dispatchers.IO).launch { ... }for fire-and-forget (but loses error propagation)- Accept a
CoroutineScopeparameter for structured concurrencyapp/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (1)
414-432: Critical: HttpClient getter still creates new instance on each access.The
clientproperty usesget() =which instantiates a newHttpClienton every access. InsendWebhook():
- Line 302:
client.post(url)creates HttpClient #1- Line 369:
client.close()creates HttpClient #2 and closes itHttpClient #1 is never closed, causing connection/thread leaks across the batch.
- private val client - get() = HttpClient(OkHttp) { + private val client by lazy { + HttpClient(OkHttp) { install(HttpTimeout) { requestTimeoutMillis = PROCESSING_TIMEOUT_MS connectTimeoutMillis = 5000 socketTimeoutMillis = 5000 } install(ContentNegotiation) { gson { configure() } } install(DefaultRequest) { userAgent("${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME}") } install(PayloadSingingPlugin) { secretKeyProvider = { settings.signingKey } } } + }Then remove
client.close()from the finally block insendWebhook()(line 369) and add cleanup at the end ofdoWork()before returning success.
🧹 Nitpick comments (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt (1)
59-83: Consider handling missing webhook in scheduleRetry.
dao.getById(webhookId)at line 65 may throw if the webhook was deleted. The caller (handleWebhookFailure) catches exceptions, but explicit handling would be cleaner.suspend fun scheduleRetry( webhookId: Long, error: String?, maxRetries: Int = 3, baseDelayMs: Long = 5000L ) { - val webhook = dao.getById(webhookId) + val webhook = dao.getById(webhookId) ?: run { + // Webhook was deleted, nothing to retry + return + }This requires changing
getByIdto returnWebhookQueueEntity?or keeping the current behavior where the exception propagates tohandleWebhookFailure.app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
157-159: CatchingThrowableincludes unrecoverable JVM errors.Similar to the issue fixed in
WebhookQueueProcessorWorker, consider catchingExceptioninstead.- } catch (e: Throwable) { + } catch (e: Exception) { e.printStackTrace() return Result.Retry(e.message ?: e.toString())
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
getForegroundInfo(167-169)app/src/main/java/me/capcom/smsgateway/extensions/GsonBuilder.kt (1)
configure(7-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build insecure / build-apk
- GitHub Check: Build release / build-apk
🔇 Additional comments (6)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (3)
95-99: LGTM: ExistingWorkPolicy.KEEP prevents interrupting active processing.Using
KEEPensures an already-running worker won't be cancelled mid-batch, preventing webhooks from getting stuck in "processing" state.
122-131: LGTM: setForeground wrapped in try-catch.Properly handles
ForegroundServiceStartNotAllowedExceptionon Android 12+ without crashing the worker.
356-367: LGTM: Catching Exception instead of Throwable.Properly catches recoverable exceptions while allowing critical JVM errors to propagate.
app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt (1)
7-26: LGTM: Clean repository pattern with proper initialization.The repository correctly encapsulates DAO operations and initializes webhooks with
PENDINGstatus and current timestamp for immediate processing eligibility.app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
249-291: LGTM: Robust fallback mechanism.The fallback to direct
WorkManagerprocessing when queue enqueue fails ensures webhooks aren't lost. Proper logging and storage of payload inTemporaryStoragemaintains data integrity.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt (1)
12-44: LGTM: Well-designed entity with appropriate indexes.The composite indexes on
(status, next_attempt)and(status, created_at)optimize the query patterns for fetching pending webhooks and cleanup operations. The schema supports the retry/backoff mechanism effectively.
9a68298 to
12487c6
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
210-231: AvoidrunBlockinginsidestart()to prevent blocking coroutine threads.
start()usesrunBlockingto call the suspendenqueueWebhook(). Whenstart()is invoked from coroutine-based flows (as inEventsReceiver/WebHooksService), this blocks the dispatcher thread and can hurt responsiveness or deadlock if used on a constrained dispatcher.Prefer exposing a suspend API (e.g.
suspend fun start(...)) or delegating enqueueing to a caller-ownedCoroutineScopeinstead ofrunBlocking.app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json (1)
331-414: Webhook queue schema matches the entity; revisit payload limits and cleanup indexing.The
webhook_queuetable definition and its composite indices line up withWebhookQueueEntityand the DAO queries (status + next_attemptandstatus + created_at), so the schema is consistent.Operationally, the earlier points still apply:
retry_countis tracked but max retries are enforced only in application logic; make sure this coupling is documented.payloadis an unboundedTEXT; consider validating payload size at enqueue time to avoid unbounded growth.- Cleanup queries rely on
created_at; if you see heavy cleanup traffic, a dedicatedcreated_atindex (in addition to the composite one) could be worthwhile.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt (2)
20-21: MakegetByIdreturn nullable to avoid crashes on missing rows.Declaring
getByIdas returning a non-nullWebhookQueueEntitymeans Room will throw if the ID is not found. ReturningWebhookQueueEntity?lets callers handle “not found” gracefully.@Query("SELECT * FROM webhook_queue WHERE id = :id") suspend fun getById(id: Long): WebhookQueueEntity?Then adjust callers to handle a null result.
48-57: AlignmarkAsProcessingandrecoverStuckProcessingWebhooksto avoid premature recovery.
recoverStuckProcessingWebhooksusesnext_attempt < :timeoutThresholdto detect stuck items, butmarkAsProcessingonly flipsstatus = "processing"and leavesnext_attemptunchanged. Under backlog, a webhook scheduled long ago but just started processing could immediately satisfy the timeout condition and be reset topendingwhile still in-flight.Consider updating
next_attemptwhen marking as processing so the timeout is measured from processing start, e.g.:@Query( """ UPDATE webhook_queue SET status = 'processing', next_attempt = :currentTime WHERE id = :id """ ) suspend fun markAsProcessing( id: Long, currentTime: Long = System.currentTimeMillis(), )Also applies to: 136-148
🧹 Nitpick comments (4)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
48-53: Consider adding a Kotlin@Deprecatedannotation in addition to KDoc.You’ve documented deprecation in KDoc, but callers won’t get compiler warnings. Consider adding a
@Deprecatedannotation onSendWebhookWorkerwith a message and replacement (e.g. queue-based processing), so usage is surfaced at compile time.app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (1)
126-210: Emit logic and logging are solid; consider unifying logmodulenaming.The new
emit()flow correctly:
- Filters webhooks by enabled source and available
deviceId.- Builds
WebHookEventDTOper webhook.- Queues via
SendWebhookWorker.start()inside a per-item try/catch so one failure doesn’t short-circuit others.- Logs both per-webhook results and a final summary.
One small nit: here you use
"WebHooksService"as themodulevalue in logs, while other places (e.g.SendWebhookWorker) useNAME = "webhooks". Aligning on a single module identifier would make log querying/aggregation easier.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt (1)
12-44: Entity mapping is consistent; consider sharing a single timestamp source if needed.
WebhookQueueEntitymatches thewebhook_queueschema and indices well, and the defaults forretryCount,status, and timestamps are sensible.If you ever need
createdAtandnextAttemptto be exactly identical on initial insert, consider deriving both from a single timestamp in the repository/factory instead of separateSystem.currentTimeMillis()defaults in the constructor.app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt (1)
110-122: Guard queue statistics aggregates against NULL on empty tables.
getQueueStatistics()usesSUM(CASE WHEN ...)projections mapped into non-nullIntfields. Depending on SQLite behavior,SUM(...)can returnNULLwhen there are no rows, which may not map cleanly into non-null KotlinInts.To be defensive, you could wrap each aggregate with
IFNULL(..., 0)so statistics are always zero-based when the queue is empty.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json(1 hunks)app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt(3 hunks)app/src/main/java/me/capcom/smsgateway/data/Module.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/Module.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookConverters.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt(1 hunks)app/src/main/res/values-ru/strings.xml(6 hunks)app/src/main/res/values-zh/strings.xml(1 hunks)app/src/main/res/values/strings.xml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
- app/src/main/java/me/capcom/smsgateway/data/Module.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookConverters.kt
- app/src/main/res/values-ru/strings.xml
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt
- app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
- app/src/main/res/values-zh/strings.xml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build insecure / build-apk
- GitHub Check: Build release / build-apk
🔇 Additional comments (4)
app/src/main/res/values/strings.xml (1)
85-85: String resource addition looks good.The new
processing_webhook_queuestring is correctly placed near related webhook strings and follows project naming conventions. The ellipsis usage aligns with the pattern for ongoing operations throughout the file. All corresponding localized strings have been added: Chinese locale contains "处理 Webhook 队列中…" and Russian locale contains "Обработка очереди вебхуков…".app/src/main/java/me/capcom/smsgateway/modules/webhooks/Module.kt (1)
3-10: DI binding forWebhookQueueRepositorylooks correct.Singleton registration via
singleOf(::WebhookQueueRepository)is consistent with the existingWebHooksServicebinding and should work fine with Koin injection.app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
249-291: Fallback path on enqueue failure is well thought out.On repository enqueue failure you:
- Log a WARN with error details and URL.
- Persist the payload to
TemporaryStorageunder a stable key (data.id).- Recreate the original
SendWebhookWorkerOneTimeWorkRequest with backoff, expedited policy, and optional network constraint.This preserves delivery behavior even if the new queue infrastructure fails during enqueue.
app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (1)
36-45: Queue processor lifecycle wiring looks good.Starting
WebhookQueueProcessorWorkeralongsideReviewWebhooksWorkerand stopping it instop()keeps the processor aligned with the webhook module’s lifecycle and should avoid orphaned workers.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (3)
163-168: Consider passing Context as parameter for consistency.The emit method uses
get<Context>()to retrieve Context from Koin, while start/stop methods receive Context as a parameter. For consistency and easier testing, consider passing Context to emit() instead.- fun emit(event: WebHookEvent, payload: Any) { + fun emit(context: Context, event: WebHookEvent, payload: Any) { val webhooksToProcess = webHooksDao.selectByEvent(event) var queuedCount = 0 var skippedCount = 0 webhooksToProcess.forEach { webhook -> // ... skip logic ... try { val webhookEventDTO = WebHookEventDTO( id = NanoIdUtils.randomNanoId(), webhookId = webhook.id, event = event, deviceId = deviceId, payload = payload, ) SendWebhookWorker.start( - context = get<Context>(), + context = context, url = webhook.url, data = webhookEventDTO, internetRequired = webhooksSettings.internetRequired )
172-181: Consider reducing logging verbosity for normal operations.The per-webhook success log at DEBUG level may generate excessive logs when many webhooks are configured. Consider removing individual success logs and relying on the summary, or making them conditional on a verbose debug flag.
- logsService.insert( - LogEntry.Priority.DEBUG, - NAME, - "Queued webhook event for processing", - mapOf( - "webhookId" to webhook.id, - "event" to event.name, - "internetRequired" to webhooksSettings.internetRequired - ) - ) + // Individual success logs removed; rely on summary
197-210: Log summary at higher priority when webhooks are skipped.The summary always logs at DEBUG level, even when webhooks fail. Consider logging at WARN or ERROR when skippedCount > 0 to make failures more visible in production logs.
// Log summary for debugging if (webhooksToProcess.isNotEmpty()) { + val priority = if (skippedCount > 0) LogEntry.Priority.WARN else LogEntry.Priority.DEBUG logsService.insert( - LogEntry.Priority.DEBUG, + priority, NAME, "Webhook emission summary", mapOf( "event" to event.name, "totalWebhooks" to webhooksToProcess.size, "queued" to queuedCount, "skipped" to skippedCount ) ) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt(3 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt(4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build release / build-apk
- GitHub Check: Build insecure / build-apk
🔇 Additional comments (8)
app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt (4)
17-18: Webhook queue imports look consistentThe new imports for
WebhookQueueDaoandWebhookQueueEntityalign with the additions below and avoid fully qualified usage; no issues here.
21-29: Entity registration forWebhookQueueEntityis appropriateAdding
WebhookQueueEntityto theentitiesarray is the right way to bring the webhook queue table under Room management. Assuming its fields either use existingConvertersor are annotated with their own@TypeConverters, this setup should be sufficient.Please double‑check that
WebhookQueueEntitycompiles cleanly with the current@TypeConverters(Converters::class)configuration (or its own per‑entity converters) and that no additional database‑wide converters are required.
30-48: Version bump and auto‑migration path 16→17Bumping the DB version to
17and declaringAutoMigration(from = 16, to = 17)keeps the migration chain continuous alongside the existing manual migrations (7→8, 13→14). For a new queue table, an auto‑migration is typically appropriate and should be generated by Room.Please confirm that:
- Room successfully generates the 16→17 auto‑migration during compilation, and
- A test/device upgrade from an existing v16 database to v17 runs without migration errors (e.g., via your existing migration test setup or a manual upgrade on a debug build).
52-55: DAO accessor forWebhookQueueDaois correctly exposedAdding
abstract fun webhookQueueDao(): WebhookQueueDaomatches the new DAO and is consistent with the other DAO accessors.app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (4)
11-12: LGTM! Dependencies correctly added for queue-based webhook processing.The LogsService and WebhookQueueProcessorWorker imports, along with the constructor parameter, properly support the new queuing architecture.
Also applies to: 21-21, 32-32
126-151: LGTM! Multi-webhook processing with proper skip logic.The rewritten emit method correctly processes all webhooks for an event, with appropriate skip logic for disabled sources and missing device IDs.
36-46: Worker lifecycle management is correctly implemented.The WebhookQueueProcessorWorker.start() method properly accepts both context and internetRequired parameters, and correctly applies network constraints when required (lines 85-91). The stop() method properly cancels the worker through WorkManager. Queue processing with batch handling, retry logic, and error handling is complete.
153-194: No action needed. SendWebhookWorker persists webhook events to the database queue before any potential failure points. The start() method (lines 225-230) enqueues to the persistent queue immediately, and includes a fallback mechanism with exponential backoff if queue enqueue fails (lines 265-277). Exception handling in WebHooksService lines 182-194 properly logs errors from the start() call itself, but the webhook data is already safely persisted by that point.Likely an incorrect or invalid review comment.
05189c9 to
af8c0f0
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (6)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
225-230:runBlockingblocks a coroutine dispatcher thread.This concern was already raised in a previous review. The
start()function is invoked from coroutine scopes inEventsReceiver, so usingrunBlockinghere blocks a dispatcher thread unnecessarily. Consider makingstart()a suspend function or using a different approach to avoid blocking.app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json (1)
331-414: Webhook queue table schema is well-designed.The
webhook_queuetable structure is appropriate for the persistent queue pattern:
- Auto-increment primary key for ordering
- Proper fields for retry tracking (
retry_count,next_attempt,last_error)- Status field with sensible default (
'pending')- Composite indices on
(status, next_attempt)and(status, created_at)align with expected query patternsPrevious review comments regarding retry limit enforcement at the application level, payload size validation, and cleanup query optimization remain valid considerations for future iterations.
app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt (2)
20-21:getByIdwill crash if the record doesn't exist.Room will throw an exception if no row matches the ID when the return type is non-nullable. Return nullable to handle missing records gracefully.
Apply this diff:
@Query("SELECT * FROM webhook_queue WHERE id = :id") - suspend fun getById(id: Long): WebhookQueueEntity + suspend fun getById(id: Long): WebhookQueueEntity?
48-57: Recovery logic measures timeout from schedule time, not processing start.
markAsProcessing()doesn't updatenext_attempt, sorecoverStuckProcessingWebhooks()at lines 136-148 checksnext_attempt < :timeoutThresholdagainst when webhooks were scheduled, not when they started processing. This can incorrectly recover webhooks that just started.Update
markAsProcessingto setnext_attemptto current time:@Query( """ UPDATE webhook_queue - SET status = "processing" + SET status = 'processing', next_attempt = :currentTime WHERE id = :id """ ) suspend fun markAsProcessing( id: Long, + currentTime: Long = System.currentTimeMillis() )app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
414-432: Critical:clientgetter creates new HttpClient on each access, causing resource leaks.The
clientproperty is a getter that instantiates a new HttpClient every time it's accessed:
- Line 302:
client.post(url)creates HttpClient #1- Line 369:
client.close()creates HttpClient #2 and closes itHttpClient #1 is never closed, leaking connections and threads.
Use lazy initialization:
- private val client - get() = HttpClient(OkHttp) { + private val client by lazy { + HttpClient(OkHttp) { install(HttpTimeout) { requestTimeoutMillis = PROCESSING_TIMEOUT_MS connectTimeoutMillis = 5000 socketTimeoutMillis = 5000 } install(ContentNegotiation) { gson { configure() } } install(DefaultRequest) { userAgent("${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME}") } install(PayloadSingingPlugin) { secretKeyProvider = { settings.signingKey } } } + }Then remove the
finallyblock fromsendWebhook()(see next comment) and optionally close the client once at the end ofdoWork()after line 166:// Before return@withContext Result.success() at line 166 if (::client.isInitialized) { client.close() }
368-370: Removeclient.close()from the finally block.Once the client is lazily initialized (per the previous comment), closing it here would prematurely terminate the client for subsequent webhooks in the batch. Remove this finally block entirely.
Apply this diff:
false // Network errors should trigger retry - } finally { - client.close() - } + } }
🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
210-210: Consider reusing Gson instance from class scope.The
gsoninstance in the companion object duplicates the pattern used insendData()(line 138). Consider consolidating to a single shared instance to avoid redundant configuration.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json(1 hunks)app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt(3 hunks)app/src/main/java/me/capcom/smsgateway/data/Module.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/EventsReceiver.kt(2 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/Module.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookConverters.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueDao.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt(1 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt(4 hunks)app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt(1 hunks)app/src/main/res/values-ru/strings.xml(6 hunks)app/src/main/res/values-zh/strings.xml(1 hunks)app/src/main/res/values/strings.xml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
- app/src/main/res/values-ru/strings.xml
- app/src/main/java/me/capcom/smsgateway/data/Module.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueEntity.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/Module.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookStatus.kt
- app/src/main/res/values/strings.xml
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookConverters.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebhookQueueRepository.kt
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (1)
getForegroundInfo(167-169)app/src/main/java/me/capcom/smsgateway/extensions/GsonBuilder.kt (1)
configure(7-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build insecure / build-apk
- GitHub Check: Build release / build-apk
🔇 Additional comments (11)
app/src/main/res/values-zh/strings.xml (1)
88-88: Localization addition looks good.The new Chinese string for webhook queue processing is correctly formatted, properly positioned, and the translation aligns with the pattern of similar UI status strings (e.g., "sending_webhook" on line 87).
app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt (1)
17-18: LGTM! Database extension for webhook queue is well-structured.The Room database changes correctly add the new
WebhookQueueEntityto the entities list, bump the version to 17, and useAutoMigrationfor the 16→17 transition. Since thewebhook_queuetable is entirely new with no column alterations to existing tables,AutoMigrationis appropriate here.Also applies to: 27-27, 30-30, 47-47, 54-54
app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt (1)
102-102: LGTM! Updated emit call with context parameter.The change correctly passes the
contextparameter (already available inprocess()) to the updatedWebHooksService.emitsignature, enabling context-aware webhook queue processing.app/src/main/java/me/capcom/smsgateway/modules/webhooks/EventsReceiver.kt (2)
70-72: Same Context injection pattern - verify consistency.This
get()call uses the same pattern as line 24. The verification above applies here as well.
23-27: Context is properly registered in the Koin DI container viaandroidContext(this@App)inApp.kt(line 33). Theget()call correctly resolves to the registered Application context through type inference from theemit()function's first parameter (Context). NoNoBeanDefFoundExceptionwill be thrown at runtime.Likely an incorrect or invalid review comment.
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/SendWebhookWorker.kt (2)
48-51: Good practice: Clear deprecation notice with removal date.The
@deprecatedannotation with a specific removal date (2026-11-30) provides clear guidance for future cleanup. This is helpful for tracking technical debt.
249-291: Good fallback mechanism for queue enqueue failures.The fallback path that stores the payload in
TemporaryStorageand usesOneTimeWorkRequestprovides resilience if the persistent queue fails. The logging at WARN level appropriately signals the degraded path.app/schemas/me.capcom.smsgateway.data.AppDatabase/17.json (1)
1-5: Schema version 17 snapshot looks complete.The schema file correctly captures the full database state at version 17, including the new
webhook_queueentity and all existing tables with their indices and foreign keys.app/src/main/java/me/capcom/smsgateway/modules/webhooks/WebHooksService.kt (1)
126-210: LGTM! Queue-based emission is well-implemented.The refactored emit method correctly:
- Processes webhooks in a loop with proper skip logic for disabled sources
- Resolves deviceId with fallback handling
- Queues each webhook via SendWebhookWorker with comprehensive error handling
- Logs per-webhook and summary statistics for debugging
The try-catch per webhook ensures one failure doesn't block others, and the counters provide good observability.
app/src/main/java/me/capcom/smsgateway/modules/webhooks/workers/WebhookQueueProcessorWorker.kt (2)
110-180: LGTM! Worker lifecycle and error handling are well-structured.The
doWork()method correctly:
- Wraps foreground service setup in try-catch (lines 122-131) to handle Android 12+ restrictions
- Uses
NonCancellablecontext for queue processing to prevent partial state- Implements backoff delay when queue is empty
- Cleans up old entries before completion
- Returns
Result.retry()with linear backoff on exceptions
95-99: Good choice usingExistingWorkPolicy.KEEP.Using
KEEPprevents interrupting an active worker mid-batch, which would leave webhooks stuck in "processing" state. This is the correct policy for this use case.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.