Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backend: improve inconsistency edge case #251

Merged
merged 2 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions src/GWallet.Backend.Tests/FaultTolerance.fs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,39 @@ type FaultTolerance() =
|> ignore<int> )
Assert.That(inconsistencyEx.Message, IsString.WhichContains "received: 4, consistent: 2, required: 3")

[<Test>]
member __.``should not throw inconsistency exception if received results is less than required``() =
let numberOfConsistentResponsesToBeConsideredSafe = 2u
let consistencyConfig =
SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe |> Some
let settings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig

let someResult = 1

let aJob1 =
async { return someResult }
let aJob2 =
async { return raise SomeSpecificException }
let aJob3 =
async { return raise SomeSpecificException }

let func1,func2,func3 = serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob1" aJob1,
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob2" aJob2,
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob3" aJob3

let client = FaultTolerantParallelClient<ServerDetails, SomeSpecificException>
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test

let notEngoughAvailableEx =
Assert.Throws<NotEnoughAvailableException>(fun _ ->
client.Query
settings
[ func1; func2; func3 ]
|> Async.RunSynchronously
|> ignore<int>
)
Assert.That(notEngoughAvailableEx.Message, IsString.WhichContains "received: 1, unavailable: 2, required: 2")

[<Test>]
member __.``using an average func instead of consistency``() =
let job1 =
Expand Down
10 changes: 5 additions & 5 deletions src/GWallet.Backend/Account.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ open System.Threading.Tasks
open GWallet.Backend.FSharpUtil.UwpHacks

// this exception, if it happens, it would cause a crash because we don't handle it yet
type InconsistentResultFromDifferentServersOfSameCurrency(currency: Currency,
innerException: ResultInconsistencyException) =
inherit Exception (SPrintF2 "Inconsistent results retrieving info for currency %A: %s"
type UnhandledCurrencyServerException(currency: Currency,
innerException: Exception) =
inherit Exception (SPrintF2 "Issue when retrieving %A currency balance: %s"
currency
innerException.Message,
innerException)
Expand Down Expand Up @@ -55,8 +55,8 @@ module Account =
balance
return Fresh compoundBalance
with
| :? ResultInconsistencyException as innerEx ->
return raise <| InconsistentResultFromDifferentServersOfSameCurrency(account.Currency, innerEx)
| ex ->
return raise <| UnhandledCurrencyServerException(account.Currency, ex)
}

let internal GetAccountFromFile accountFile (currency: Currency) kind: IAccount =
Expand Down
50 changes: 39 additions & 11 deletions src/GWallet.Backend/FaultTolerantParallelClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,40 @@ open Fsdk

open GWallet.Backend.FSharpUtil.UwpHacks

type ResourcesUnavailabilityException (message: string, innerOrLastException: Exception) =
inherit Exception (message, innerOrLastException)
type ResourcesUnavailabilityException =
inherit Exception

new(message: string, innerException: Exception) = { inherit Exception(message, innerException) }
new(message: string) = { inherit Exception(message) }

type private TaskUnavailabilityException (message: string, innerException: Exception) =
inherit ResourcesUnavailabilityException (message, innerException)

type private ServersUnavailabilityException (message: string, lastException: Exception) =
inherit ResourcesUnavailabilityException (message, lastException)
type ServersUnavailabilityException =
inherit ResourcesUnavailabilityException

new(message: string, innerException: Exception) = { inherit ResourcesUnavailabilityException(message, innerException) }
new(message: string) = { inherit ResourcesUnavailabilityException(message) }

type private NoneAvailableException (message:string, lastException: Exception) =
inherit ServersUnavailabilityException (message, lastException)

type private NotEnoughAvailableException (message:string, lastException: Exception) =
inherit ServersUnavailabilityException (message, lastException)
type NotEnoughAvailableException =
inherit ServersUnavailabilityException

new (message: string, innerException: Exception) =
{ inherit ServersUnavailabilityException (message, innerException) }
new (totalNumberOfSuccesfulResultsObtained: uint32,
numberOfServersUnavailable: uint32,
numberOfConsistentResultsRequired: uint32) =
{ inherit ServersUnavailabilityException ("Results obtained were not enough to be considered consistent" +
SPrintF3 " (received: %i, unavailable: %i, required: %i)"
totalNumberOfSuccesfulResultsObtained
numberOfServersUnavailable
numberOfConsistentResultsRequired)
}

type ResultInconsistencyException (totalNumberOfSuccesfulResultsObtained: int,
type ResultInconsistencyException (totalNumberOfSuccesfulResultsObtained: uint32,
maxNumberOfConsistentResultsObtained: int,
numberOfConsistentResultsRequired: uint32) =
inherit Exception ("Results obtained were not enough to be considered consistent" +
Expand Down Expand Up @@ -591,7 +609,8 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio
retriesForInconsistency
cancellationSource
else
let totalNumberOfSuccesfulResultsObtained = executedServers.SuccessfulResults.Length
let totalNumberOfSuccesfulResultsObtained = uint32 executedServers.SuccessfulResults.Length
let totalNumberOfUnavailableServers = uint32 failedFuncs.Length

// HACK: we do this as a quick fix wrt new OneServerConsistentWithCertainValueOrTwoServers setting, but we should
// (TODO) rather throw a specific overload of ResultInconsistencyException about this mode being used
Expand All @@ -609,9 +628,18 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio
return failwith "resultsSoFar.Length != 0 but MeasureConsistency returns None, please report this bug"
| (_,maxNumberOfConsistentResultsObtained)::_ ->
if (retriesForInconsistency = settings.NumberOfRetriesForInconsistency) then
return raise (ResultInconsistencyException(totalNumberOfSuccesfulResultsObtained,
maxNumberOfConsistentResultsObtained,
numberOfConsistentResponsesRequired))
if totalNumberOfSuccesfulResultsObtained >= numberOfConsistentResponsesRequired then
return raise (ResultInconsistencyException(totalNumberOfSuccesfulResultsObtained,
maxNumberOfConsistentResultsObtained,
numberOfConsistentResponsesRequired))
else
return
raise
<| NotEnoughAvailableException(
totalNumberOfSuccesfulResultsObtained,
totalNumberOfUnavailableServers,
numberOfConsistentResponsesRequired
)
else
return! QueryInternalImplementation
settings
Expand Down
Loading