Skip to content

Commit

Permalink
[trello.com/c/0dgGzsPE]: timeouts for sending messages/attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisBenua committed Feb 7, 2025
1 parent 7e455e3 commit dd90046
Show file tree
Hide file tree
Showing 20 changed files with 225 additions and 17 deletions.
6 changes: 5 additions & 1 deletion Adamant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,9 @@
AA33BEB62D303E240083E59C /* APICoreProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */; };
AA33BEB72D3041A30083E59C /* AddressConverterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */; };
AA33BEB92D3044760083E59C /* BtcApiServiceProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */; };
AA8FFFCC2D50D503001D8576 /* NodeOrigin+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */; };
AA8FFFCA2D4E6435001D8576 /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = AA8FFFC92D4E6435001D8576 /* CryptoSwift */; };
AA8FFFCC2D50D503001D8576 /* NodeOrigin+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */; };
AA8FFFE72D5543C1001D8576 /* AdmWalletService+Timeouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8FFFE62D5543BA001D8576 /* AdmWalletService+Timeouts.swift */; };
AAB01CAD2D3AE44B007D6BF4 /* BitcoinKitTransactionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CAC2D3AE449007D6BF4 /* BitcoinKitTransactionFactory.swift */; };
AAB01CAF2D3AECED007D6BF4 /* DogeWalletServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CAE2D3AECE6007D6BF4 /* DogeWalletServiceTests.swift */; };
AAB01CB12D3AF01B007D6BF4 /* DogeApiServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB01CB02D3AF015007D6BF4 /* DogeApiServiceProtocol.swift */; };
Expand Down Expand Up @@ -1133,6 +1134,7 @@
AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICoreProtocolMock.swift; sourceTree = "<group>"; };
AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcApiServiceProtocolMock.swift; sourceTree = "<group>"; };
AA8FFFCB2D50D4F8001D8576 /* NodeOrigin+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NodeOrigin+Extensions.swift"; sourceTree = "<group>"; };
AA8FFFE62D5543BA001D8576 /* AdmWalletService+Timeouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdmWalletService+Timeouts.swift"; sourceTree = "<group>"; };
AAB01CAC2D3AE449007D6BF4 /* BitcoinKitTransactionFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinKitTransactionFactory.swift; sourceTree = "<group>"; };
AAB01CAE2D3AECE6007D6BF4 /* DogeWalletServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeWalletServiceTests.swift; sourceTree = "<group>"; };
AAB01CB02D3AF015007D6BF4 /* DogeApiServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogeApiServiceProtocol.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2760,6 +2762,7 @@
E94008902119D22400CD2D67 /* Adamant */ = {
isa = PBXGroup;
children = (
AA8FFFE62D5543BA001D8576 /* AdmWalletService+Timeouts.swift */,
93294B852AAD0E0A00911109 /* AdmWallet.swift */,
93294B862AAD0E0A00911109 /* AdmWalletService.swift */,
E993301F21354B1800CD5200 /* AdmWalletFactory.swift */,
Expand Down Expand Up @@ -3720,6 +3723,7 @@
E9AA8C02212C5BF500F9249F /* AdmWalletService+Send.swift in Sources */,
E90847332196FEA80095825D /* TransferTransaction+CoreDataProperties.swift in Sources */,
9366588D2B0AB6BD00BDB2D3 /* CoinsNodesListState.swift in Sources */,
AA8FFFE72D5543C1001D8576 /* AdmWalletService+Timeouts.swift in Sources */,
AAFB3CAB2D3997DD000CCCE9 /* EthApiServiceProtocol.swift in Sources */,
E99818942120892F0018C84C /* WalletViewControllerBase.swift in Sources */,
3AA6DF462BA9BEB700EA2E16 /* MediaContentView.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion Adamant/App/DI/AppAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ struct AppAssembly: MainThreadAssembly {
accountsProvider: r.resolve(AccountsProvider.self)!,
transactionService: r.resolve(ChatTransactionService.self)!,
securedStore: r.resolve(SecuredStore.self)!,
walletServiceCompose: r.resolve(WalletServiceCompose.self)!
walletServiceCompose: r.resolve(WalletServiceCompose.self)!,
timeouts: AdmWalletService.timeouts
)
}.inObjectScope(.container)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ extension AdmWalletService {
4000
}

static var timeouts: MessageTimeouts {
MessageTimeouts(message: 300, attachment: 300)
}

var tokenName: String {
"ADAMANT Messenger"
}
Expand Down
16 changes: 16 additions & 0 deletions Adamant/Modules/Wallets/Adamant/AdmWalletService+Timeouts.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// AdmWalletService+Timeouts.swift
// Adamant
//
// Created by Christian Benua on 06.02.2025.
// Copyright © 2025 Adamant. All rights reserved.
//

import Foundation

extension AdmWalletService {
struct MessageTimeouts {
let message: TimeInterval
let attachment: TimeInterval
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ private extension AdamantPushNotificationsTokenService {
) else { return nil }

Task {
switch await apiService.sendMessageTransaction(transaction: messageTransaction) {
switch await apiService.sendMessageTransaction(transaction: messageTransaction, timeout: nil) {
case .success:
completion(true)
case .failure:
Expand Down
18 changes: 16 additions & 2 deletions Adamant/Services/DataProviders/AdamantChatsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ actor AdamantChatsProvider: ChatsProvider {
private let adamantCore: AdamantCore
private let transactionService: ChatTransactionService
private let walletServiceCompose: WalletServiceCompose
private let timeouts: AdmWalletService.MessageTimeouts

let accountService: AccountService
let accountsProvider: AccountsProvider
Expand Down Expand Up @@ -80,7 +81,8 @@ actor AdamantChatsProvider: ChatsProvider {
accountsProvider: AccountsProvider,
transactionService: ChatTransactionService,
securedStore: SecuredStore,
walletServiceCompose: WalletServiceCompose
walletServiceCompose: WalletServiceCompose,
timeouts: AdmWalletService.MessageTimeouts
) {
self.accountService = accountService
self.apiService = apiService
Expand All @@ -91,6 +93,7 @@ actor AdamantChatsProvider: ChatsProvider {
self.transactionService = transactionService
self.securedStore = securedStore
self.walletServiceCompose = walletServiceCompose
self.timeouts = timeouts

Task {
await setupSecuredStore()
Expand Down Expand Up @@ -1286,7 +1289,12 @@ extension AdamantChatsProvider {
transaction.transactionId = locallyID
transaction.chatMessageId = locallyID

let id = try await apiService.sendMessageTransaction(transaction: signedTransaction).get()
let id = try await apiService.sendMessageTransaction(
transaction: signedTransaction,
timeout: transaction.isFileTransfer
? timeouts.attachment
: timeouts.message
).get()

// Update ID with recieved, add to unconfirmed transactions.
transaction.transactionId = String(id)
Expand Down Expand Up @@ -1962,4 +1970,10 @@ extension AdamantChatsProvider {
}
}

extension ChatTransaction {
var isFileTransfer: Bool {
(self as? RichMessageTransaction)?.additionalType == RichAdditionalType.file
}
}

private let requestRepeatDelay: TimeInterval = 2
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,10 @@ extension AdamantTransfersProvider {
}

do {
let id = try await apiService.sendMessageTransaction(transaction: signedTransaction).get()
let id = try await apiService.sendMessageTransaction(
transaction: signedTransaction,
timeout: nil
).get()
transaction.transactionId = String(id)
await chatsProvider?.addUnconfirmed(transactionId: id, managedObjectId: transaction.objectID)

Expand Down
20 changes: 20 additions & 0 deletions CommonKit/Scripts/CoinsScript.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ def writeToSwiftFile(name, json)
oldPendingAttempts = txFetchInfo["oldPendingAttempts"]
end

# timeouts
timeouts = json["timeout"]

messageTimeout = nil
attachmentTimeout = nil

if !timeouts.nil?
messageTimeout = timeouts["message"] / 1000
attachmentTimeout = timeouts["attachment"] / 1000
end

# Gas for eth
reliabilityGasPricePercent = json["reliabilityGasPricePercent"]
reliabilityGasLimitPercent = json["reliabilityGasLimitPercent"]
Expand Down Expand Up @@ -228,6 +239,15 @@ def writeToSwiftFile(name, json)
emptyText
}
#{timeouts ?
createSwiftVariable(
"timeouts",
"MessageTimeouts(message: #{messageTimeout}, attachment: #{attachmentTimeout})",
"MessageTimeouts",
true
) : emptyText
}
#{reliabilityGasPricePercent ?
createSwiftVariable("reliabilityGasPricePercent", reliabilityGasPricePercent, "BigUInt", false) :
emptyText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,9 @@
/* Shared error: Network problems. In most cases - no connection */
"Error.NoNetwork" = "Keine Verbindung";

/* Shared error: Timeout Problem. In most cases - no connection */
"Error.TimeOut" = "Das Zeitlimit für die Antwort wurde überschritten";

/* Shared error: Request cancelled */
"Error.RequestCancelled" = "Request cancelled";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,9 @@
/* Shared error: Network problems. In most cases - no connection */
"Error.NoNetwork" = "No connection";

/* Shared error: Timeout Problem. In most cases - no connection */
"Error.TimeOut" = "Response waiting time exceeded";

/* Shared error: Request cancelled */
"Error.RequestCancelled" = "Request cancelled";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,9 @@
/* Shared error: Network problems. In most cases - no connection */
"Error.NoNetwork" = "Нет соединения с сетью";

/* Shared error: Timeout Problem. In most cases - no connection */
"Error.TimeOut" = "Превышено время ожидания ответа";

/* Shared error: Request cancelled */
"Error.RequestCancelled" = "Запрос отменён";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,9 @@
/* Shared error: Network problems. In most cases - no connection */
"Error.NoNetwork" = "无连接";

/* Shared error: Timeout Problem. In most cases - no connection */
"Error.TimeOut" = "超过响应等待时间";

/* Shared error: Request cancelled */
"Error.RequestCancelled" = "请求已取消";

Expand Down
76 changes: 76 additions & 0 deletions CommonKit/Sources/CommonKit/Helpers/Deadline.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Deadline.swift
// CommonKit
//
// Created by Christian Benua on 06.02.2025.
//

import Foundation
import QuartzCore

public func deadline<R>(
until instant: TimeInterval,
isolation: isolated (any Actor)? = #isolation,
operation: @Sendable () async throws -> R
) async throws -> R where R: Sendable {
let result = await withoutActuallyEscaping(operation) { operation in
await withTaskGroup(
of: DeadlineState<R>.self,
returning: Result<R, any Error>.self,
isolation: isolation
) { taskGroup in

taskGroup.addTask {
do {
let result = try await operation()
return .operationResult(.success(result))
} catch {
return .operationResult(.failure(error))
}
}

taskGroup.addTask {
do {
let interval = instant - CACurrentMediaTime()
guard interval > 0 else {
return .sleepResult(.failure(DeadlineExceededError()))
}
try await Task.sleep(interval: interval)
return .sleepResult(.success(false))
} catch where Task.isCancelled {
return .sleepResult(.success(true))
} catch {
return .sleepResult(.failure(error))
}
}

defer {
taskGroup.cancelAll()
}

for await next in taskGroup {
switch next {
case .operationResult(let result):
return result
case .sleepResult(.success(false)):
return .failure(DeadlineExceededError())
case .sleepResult(.success(true)):
continue
case .sleepResult(.failure(let error)):
return .failure(error)
}
}

preconditionFailure("Invalid state")
}
}

return try result.get()
}

enum DeadlineState<T>: Sendable where T: Sendable {
case operationResult(Result<T, Error>)
case sleepResult(Result<Bool, Error>)
}

public struct DeadlineExceededError: Error {}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public extension String.adamant {
public static var networkError: String {
String.localized("Error.NoNetwork", comment: "Shared error: Network problems. In most cases - no connection")
}
public static var timeoutError: String {
String.localized("Error.TimeOut", comment: "Shared error: Timeout Problem. In most cases - no connection")
}
public static var requestCancelled: String {
String.localized("Error.RequestCancelled", comment: "Shared error: Request cancelled")
}
Expand Down
6 changes: 5 additions & 1 deletion CommonKit/Sources/CommonKit/Models/ApiServiceError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ extension ApiServiceError: Equatable {
}
}

extension ApiServiceError: HealthCheckableError {
extension ApiServiceError: HealthCheckableTimeoutableError {
public var isNetworkError: Bool {
switch self {
case .networkError:
Expand All @@ -96,6 +96,10 @@ extension ApiServiceError: HealthCheckableError {
.networkError(error: AdamantError(message: .adamant.sharedErrors.networkError))
}

public static var timeoutError: ApiServiceError {
.networkError(error: AdamantError(message: .adamant.sharedErrors.timeoutError))
}

public static func noEndpointsError(nodeGroupName: String) -> ApiServiceError {
.noEndpointsAvailable(nodeGroupName: nodeGroupName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,13 @@ public protocol AdamantApiServiceProtocol: ApiServiceProtocol {

func sendTransaction(
path: String,
transaction: UnregisteredTransaction
transaction: UnregisteredTransaction,
timeout: TimeInterval?
) async -> ApiServiceResult<UInt64>

func sendMessageTransaction(
transaction: UnregisteredTransaction
transaction: UnregisteredTransaction,
timeout: TimeInterval?
) async -> ApiServiceResult<UInt64>

// MARK: - Delegates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ extension AdamantApiService {
}

public func sendMessageTransaction(
transaction: UnregisteredTransaction
transaction: UnregisteredTransaction,
timeout: TimeInterval? = nil
) async -> ApiServiceResult<UInt64> {
await sendTransaction(
path: ApiCommands.Chats.processTransaction,
transaction: transaction
transaction: transaction,
timeout: timeout
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public extension ApiCommands {
extension AdamantApiService {
public func sendTransaction(
path: String,
transaction: UnregisteredTransaction
transaction: UnregisteredTransaction,
timeout: TimeInterval? = nil
) async -> ApiServiceResult<UInt64> {
let response: ApiServiceResult<TransactionIdResponse> = await request { core, origin in
let response: ApiServiceResult<TransactionIdResponse> = await request(timeout: timeout) { core, origin in
await core.sendRequestJsonResponse(
origin: origin,
path: path,
Expand Down
Loading

0 comments on commit dd90046

Please sign in to comment.