mirror of
https://github.com/gilbertchen/duplicacy
synced 2025-12-06 00:03:38 +00:00
Better handling of B2 authorization failures
This commit fixed 2 issues wrt Backblaze B2 authorization: * every thread may call b2_authorize_account at the same time when there are 401 errors * if B2 has a login outage, then all threads will call b2_authorize_account repeatedly without delay A simple solution is to limit one b2_authorize_account call to once every 30 second regardless of how many threads there are. If the call to b2_authorize_account is not allowed, the random exponential backoff will be performed.
This commit is contained in:
@@ -62,6 +62,8 @@ type B2Client struct {
|
||||
Threads int
|
||||
MaximumRetries int
|
||||
TestMode bool
|
||||
|
||||
LastAuthorizationTime int64
|
||||
}
|
||||
|
||||
// URL encode the given path but keep the slashes intact
|
||||
@@ -253,8 +255,12 @@ func (client *B2Client) call(threadIndex int, requestURL string, method string,
|
||||
if requestURL == B2AuthorizationURL {
|
||||
return nil, nil, 0, fmt.Errorf("Authorization failure")
|
||||
}
|
||||
client.AuthorizeAccount(threadIndex)
|
||||
|
||||
// Attempt authorization again. If authorization is actually not done, run the random backoff
|
||||
_, allowed := client.AuthorizeAccount(threadIndex)
|
||||
if allowed {
|
||||
continue
|
||||
}
|
||||
} else if response.StatusCode == 403 {
|
||||
if !client.TestMode {
|
||||
return nil, nil, 0, fmt.Errorf("B2 cap exceeded")
|
||||
@@ -291,13 +297,18 @@ type B2AuthorizeAccountOutput struct {
|
||||
DownloadURL string
|
||||
}
|
||||
|
||||
func (client *B2Client) AuthorizeAccount(threadIndex int) (err error) {
|
||||
func (client *B2Client) AuthorizeAccount(threadIndex int) (err error, allowed bool) {
|
||||
client.Lock.Lock()
|
||||
defer client.Lock.Unlock()
|
||||
|
||||
// Don't authorize if the previous one was done less than 30 seconds ago
|
||||
if client.LastAuthorizationTime != 0 && client.LastAuthorizationTime > time.Now().Unix() - 30 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
readCloser, _, _, err := client.call(threadIndex, B2AuthorizationURL, http.MethodPost, nil, make(map[string]string))
|
||||
if err != nil {
|
||||
return err
|
||||
return err, true
|
||||
}
|
||||
|
||||
defer readCloser.Close()
|
||||
@@ -305,7 +316,7 @@ func (client *B2Client) AuthorizeAccount(threadIndex int) (err error) {
|
||||
output := &B2AuthorizeAccountOutput{}
|
||||
|
||||
if err = json.NewDecoder(readCloser).Decode(&output); err != nil {
|
||||
return err
|
||||
return err, true
|
||||
}
|
||||
|
||||
// The account id may be different from the application key id so we're getting the account id from the returned
|
||||
@@ -317,7 +328,9 @@ func (client *B2Client) AuthorizeAccount(threadIndex int) (err error) {
|
||||
client.DownloadURL = output.DownloadURL
|
||||
client.IsAuthorized = true
|
||||
|
||||
return nil
|
||||
client.LastAuthorizationTime = time.Now().Unix()
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
type ListBucketOutput struct {
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestB2Client(t *testing.T) {
|
||||
|
||||
b2Client.TestMode = true
|
||||
|
||||
err := b2Client.AuthorizeAccount(0)
|
||||
err, _ := b2Client.AuthorizeAccount(0)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to authorize the b2 account: %v", err)
|
||||
return
|
||||
|
||||
@@ -19,7 +19,7 @@ func CreateB2Storage(accountID string, applicationKey string, bucket string, sto
|
||||
|
||||
client := NewB2Client(accountID, applicationKey, storageDir, threads)
|
||||
|
||||
err = client.AuthorizeAccount(0)
|
||||
err, _ = client.AuthorizeAccount(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user