Skip to content
Snippets Groups Projects
Commit a7943658 authored by Richard T. Carback III's avatar Richard T. Carback III
Browse files

Add error reporting. Be sure that all functions have disclaimer.

parent 2d42c016
No related branches found
No related tags found
2 merge requests!231Revert "Update store to print changes to the partners list",!171RestoreContactsFromBackup
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"strings"
"sync" "sync"
"time" "time"
...@@ -36,14 +37,50 @@ type RestoreContactsUpdater interface { ...@@ -36,14 +37,50 @@ type RestoreContactsUpdater interface {
RestoreContactsCallback(numFound, numRestored, total int, err string) RestoreContactsCallback(numFound, numRestored, total int, err string)
} }
// RestoreContactsReport is a gomobile friendly report structure
// for determining which IDs restored, which failed, and why.
type RestoreContactsReport struct {
restored []*id.ID
failed []*id.ID
errs []error
}
// LenRestored returns the length of ID's restored.
func (r *RestoreContactsReport) LenRestored() int {
return len(r.restored)
}
// LenFailed returns the length of the ID's failed.
func (r *RestoreContactsReport) LenFailed() int {
return len(r.failed)
}
// GetRestoredAt returns the restored ID at index
func (r *RestoreContactsReport) GetRestoredAt(index int) []byte {
return r.restored[index].Bytes()
}
// GetFailedAt returns the failed ID at index
func (r *RestoreContactsReport) GetFailedAt(index int) []byte {
return r.failed[index].Bytes()
}
// GetErrorAt returns the error string at index
func (r *RestoreContactsReport) GetErrorAt(index int) string {
return r.errs[index].Error()
}
// RestoreContactsFromBackup takes as input the jason output of the // RestoreContactsFromBackup takes as input the jason output of the
// `NewClientFromBackup` function, unmarshals it into IDs, looks up // `NewClientFromBackup` function, unmarshals it into IDs, looks up
// each ID in user discovery, and initiates a session reset request. // each ID in user discovery, and initiates a session reset request.
// This function will not return until every id in the list has been sent a // This function will not return until every id in the list has been sent a
// request. It should be called again and again until it completes. // request. It should be called again and again until it completes.
// xxDK users should not use this function. This function is used by
// the mobile phone apps and are not intended to be part of the xxDK. It
// should be treated as internal functions specific to the phone apps.
func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
udManager *bindings.UserDiscovery, udManager *bindings.UserDiscovery,
updatesCb RestoreContactsUpdater) error { updatesCb RestoreContactsUpdater) (*RestoreContactsReport, error) {
// Constants/control settings // Constants/control settings
numRoutines := 8 numRoutines := 8
...@@ -59,15 +96,20 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, ...@@ -59,15 +96,20 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
// Unmarshal IDs and then check restore state // Unmarshal IDs and then check restore state
var idList []*id.ID var idList []*id.ID
if err := json.Unmarshal(backupPartnerIDs, &idList); err != nil { if err := json.Unmarshal(backupPartnerIDs, &idList); err != nil {
return err return nil, err
} }
lookupIDs, resetContacts := checkRestoreState(idList, store) lookupIDs, resetContacts, restored := checkRestoreState(idList, store)
// State variables, how many we have looked up successfully // State variables, how many we have looked up successfully
// and how many we have already reset. // and how many we have already reset.
totalCnt := len(idList) totalCnt := len(idList)
lookupCnt := len(resetContacts) lookupCnt := len(resetContacts)
resetCnt := totalCnt - len(resetContacts) - len(lookupIDs) resetCnt := totalCnt - len(resetContacts) - len(lookupIDs)
report := &RestoreContactsReport{
restored: restored,
failed: make([]*id.ID, 0),
errs: make([]error, 0),
}
// Before we start, report initial state // Before we start, report initial state
updatesCb.RestoreContactsCallback(lookupCnt, resetCnt, totalCnt, "") updatesCb.RestoreContactsCallback(lookupCnt, resetCnt, totalCnt, "")
...@@ -81,6 +123,7 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, ...@@ -81,6 +123,7 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
foundCh := make(chan *contact.Contact, chanSize) foundCh := make(chan *contact.Contact, chanSize)
resetContactCh := make(chan *contact.Contact, chanSize) resetContactCh := make(chan *contact.Contact, chanSize)
restoredCh := make(chan *contact.Contact, chanSize) restoredCh := make(chan *contact.Contact, chanSize)
failCh := make(chan failure, chanSize)
// Start routines for processing // Start routines for processing
lcWg := sync.WaitGroup{} lcWg := sync.WaitGroup{}
...@@ -88,8 +131,8 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, ...@@ -88,8 +131,8 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
rsWg := sync.WaitGroup{} rsWg := sync.WaitGroup{}
rsWg.Add(numRoutines) rsWg.Add(numRoutines)
for i := 0; i < numRoutines; i++ { for i := 0; i < numRoutines; i++ {
go LookupContacts(lookupCh, foundCh, udManager, lcWg) go LookupContacts(lookupCh, foundCh, failCh, udManager, lcWg)
go ResetSessions(resetContactCh, restoredCh, api, rsWg) go ResetSessions(resetContactCh, restoredCh, failCh, api, rsWg)
} }
// Load channels based on previous state // Load channels based on previous state
...@@ -105,6 +148,18 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, ...@@ -105,6 +148,18 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
} }
}() }()
// Failure processing, done separately (in a single thread)
// because failures should not reset the timer
failWg := sync.WaitGroup{}
failWg.Add(1)
go func() {
defer failWg.Done()
for fail := range failCh {
report.failed = append(report.failed, fail.ID)
report.errs = append(report.errs, fail.Err)
}
}()
// Event Processing // Event Processing
done := false done := false
var err error var err error
...@@ -122,6 +177,7 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, ...@@ -122,6 +177,7 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
go func() { resetContactCh <- c }() go func() { resetContactCh <- c }()
case c := <-restoredCh: case c := <-restoredCh:
store.set(c, contactRestored) store.set(c, contactRestored)
report.restored = append(report.restored, c.ID)
resetCnt += 1 resetCnt += 1
} }
if resetCnt == totalCnt { if resetCnt == totalCnt {
...@@ -134,52 +190,72 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client, ...@@ -134,52 +190,72 @@ func RestoreContactsFromBackup(backupPartnerIDs []byte, client *bindings.Client,
// Cleanup // Cleanup
close(lookupCh) close(lookupCh)
close(resetContactCh) close(resetContactCh)
close(failCh)
// Now wait for subroutines to close before closing their output chans // Now wait for subroutines to close before closing their output chans
lcWg.Wait() lcWg.Wait()
close(foundCh) close(foundCh)
rsWg.Wait() rsWg.Wait()
close(restoredCh) close(restoredCh)
failWg.Wait()
return err return report, err
} }
// LookupContacts routine looks up contacts // LookupContacts routine looks up contacts
// xxDK users should not use this function. This function is used by
// the mobile phone apps and are not intended to be part of the xxDK. It
// should be treated as internal functions specific to the phone apps.
func LookupContacts(in chan *id.ID, out chan *contact.Contact, func LookupContacts(in chan *id.ID, out chan *contact.Contact,
udManager *bindings.UserDiscovery, wg sync.WaitGroup) { failCh chan failure, udManager *bindings.UserDiscovery,
wg sync.WaitGroup) {
defer wg.Done() defer wg.Done()
// Start looking up contacts with user discovery and feed this // Start looking up contacts with user discovery and feed this
// contacts channel. // contacts channel.
for lookupID := range in { for lookupID := range in {
c, err := LookupContact(lookupID, udManager) c, err := LookupContact(lookupID, udManager)
if err != nil { if err == nil {
jww.WARN.Printf("could not lookup %s: %v", lookupID,
err)
// Retry later
in <- lookupID
} else {
out <- c out <- c
continue
}
// If an error, figure out if I should report or retry
errStr := err.Error()
if strings.Contains(errStr, "failed to lookup ID") {
failCh <- failure{ID: lookupID, Err: err}
continue
} }
jww.WARN.Printf("could not lookup %s: %v", lookupID, err)
// Retry later
in <- lookupID
} }
} }
func ResetSessions(in, out chan *contact.Contact, api api.Client, // ResetSessions routine reads the in channel, sends a reset session
wg sync.WaitGroup) { // request, then marks it done by sending to the out channel.
// xxDK users should not use this function. This function is used by
// the mobile phone apps and are not intended to be part of the xxDK. It
// should be treated as internal functions specific to the phone apps.
func ResetSessions(in, out chan *contact.Contact, failCh chan failure,
api api.Client, wg sync.WaitGroup) {
defer wg.Done() defer wg.Done()
me := api.GetUser().GetContact() me := api.GetUser().GetContact()
msg := "Account reset from backup" msg := "Account reset from backup"
for c := range in { for c := range in {
_, err := api.ResetSession(*c, me, msg) _, err := api.ResetSession(*c, me, msg)
if err != nil { if err == nil {
jww.WARN.Printf("could not reset %s: %v",
c.ID, err)
in <- c
} else {
out <- c out <- c
continue
} }
// If an error, figure out if I should report or retry
// Note: Always retry here for now.
jww.WARN.Printf("could not reset %s: %v", c.ID, err)
in <- c
} }
} }
// LookupContact lookups up a contact using the user discovery manager // LookupContact lookups up a contact using the user discovery manager
// xxDK users should not use this function. This function is used by
// the mobile phone apps and are not intended to be part of the xxDK. It
// should be treated as internal functions specific to the phone apps.
func LookupContact(userID *id.ID, udManager *bindings.UserDiscovery) ( func LookupContact(userID *id.ID, udManager *bindings.UserDiscovery) (
*contact.Contact, error) { *contact.Contact, error) {
// This is a little wonky, but wait until we get called then // This is a little wonky, but wait until we get called then
...@@ -227,6 +303,11 @@ const ( ...@@ -227,6 +303,11 @@ const (
contactRestored contactRestored
) )
type failure struct {
ID *id.ID
Err error
}
//// ////
// stateStore wraps a kv and stores contact state for the restoration // stateStore wraps a kv and stores contact state for the restoration
// TODO: Right now, it uses 1 contact-per-key approach, but it might make sense // TODO: Right now, it uses 1 contact-per-key approach, but it might make sense
...@@ -272,9 +353,10 @@ func (s stateStore) get(id *id.ID) (restoreState, *contact.Contact, error) { ...@@ -272,9 +353,10 @@ func (s stateStore) get(id *id.ID) (restoreState, *contact.Contact, error) {
// stateStore END // stateStore END
func checkRestoreState(IDs []*id.ID, store stateStore) ([]*id.ID, func checkRestoreState(IDs []*id.ID, store stateStore) ([]*id.ID,
[]*contact.Contact) { []*contact.Contact, []*id.ID) {
var idsToLookup []*id.ID var idsToLookup []*id.ID
var contactsToReset []*contact.Contact var contactsToReset []*contact.Contact
var contactsRestored []*id.ID
for i := range IDs { for i := range IDs {
id := IDs[i] id := IDs[i]
idState, user, err := store.get(id) idState, user, err := store.get(id)
...@@ -289,8 +371,9 @@ func checkRestoreState(IDs []*id.ID, store stateStore) ([]*id.ID, ...@@ -289,8 +371,9 @@ func checkRestoreState(IDs []*id.ID, store stateStore) ([]*id.ID,
idsToLookup = append(idsToLookup, id) idsToLookup = append(idsToLookup, id)
case contactFound: case contactFound:
contactsToReset = append(contactsToReset, user) contactsToReset = append(contactsToReset, user)
case contactRestored:
contactsRestored = append(contactsRestored, user.ID)
} }
// Restored state means we do nothing.
} }
return idsToLookup, contactsToReset return idsToLookup, contactsToReset, contactsRestored
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment