From cc2e90fe808c05e0a3794e733c0a2fc9b3068c29 Mon Sep 17 00:00:00 2001 From: gabriel-farache Date: Tue, 12 Apr 2022 16:32:20 +0200 Subject: [PATCH] #118 ECOPROJECT-717 Implement registration-retry with exponential backoff --- internal/registration/registration.go | 51 +++++++++++++++++----- internal/registration/registration_test.go | 26 +++++++++++ 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/internal/registration/registration.go b/internal/registration/registration.go index 3da8281a..69317a51 100644 --- a/internal/registration/registration.go +++ b/internal/registration/registration.go @@ -18,7 +18,8 @@ import ( ) const ( - retryAfter = 10 + retryAfter = 10 + maxInterval = 60 ) //go:generate mockgen -package=registration -destination=mock_deregistrable.go . Deregistrable @@ -43,6 +44,7 @@ type Registration struct { lock sync.RWMutex deregistrables []Deregistrable clientCert *ClientCert + nbRetry int } func NewRegistration(deviceID string, hardware *hardware2.Hardware, dispatcherClient DispatcherClient, @@ -55,6 +57,7 @@ func NewRegistration(deviceID string, hardware *hardware2.Hardware, dispatcherCl RetryAfter: retryAfter, workloads: workloadsManager, lock: sync.RWMutex{}, + nbRetry: 0, } err := reg.CreateClientCerts() if err != nil { @@ -111,18 +114,36 @@ func (r *Registration) RegisterDevice() { } func (r *Registration) registerDeviceWithRetries(interval int64) { - ticker := time.NewTicker(time.Second * time.Duration(interval)) - for range ticker.C { - if !r.config.IsInitialConfig() { - ticker.Stop() - break - } - log.Infof("configuration has not been initialized yet. Sending registration request. DeviceID: %s;", r.deviceID) - err := r.registerDeviceOnce() - if err != nil { - log.Errorf("cannot register device. DeviceID: %s; err: %v", r.deviceID, err) + currentInterval := interval + ticker := time.NewTicker(time.Second * time.Duration(currentInterval)) + + for { + select { + case <-ticker.C: + log.Infof("Current interval: %d", currentInterval) + if !r.config.IsInitialConfig() { + ticker.Stop() + break + } + log.Infof("configuration has not been initialized yet. Sending registration request. DeviceID: %s;", r.deviceID) + err := r.registerDeviceOnce() + if err != nil { + log.Errorf("cannot register device. DeviceID: %s; err: %v", r.deviceID, err) + } + if currentInterval < maxInterval { + currentInterval = currentInterval * 2 + if currentInterval > maxInterval { + currentInterval = maxInterval + } + ticker.Stop() + ticker = time.NewTicker(time.Duration(currentInterval) * time.Second) + } + r.nbRetry++ + } + } + } func (r *Registration) registerDeviceOnce() error { @@ -172,7 +193,7 @@ func (r *Registration) registerDeviceOnce() error { var message models.MessageResponse err = json.Unmarshal(parsedResponse.Body, &message) if err != nil { - return fmt.Errorf("Cannot unmarshal registration response content: %v", err) + return fmt.Errorf("Cannot unmarshal registration response content: %v", err) } parsedContent, ok := message.Content.(map[string]interface{}) @@ -222,6 +243,12 @@ func (r *Registration) Deregister() error { return errors } +func (r *Registration) NbRetry() int { + r.lock.RLock() + defer r.lock.RUnlock() + return r.nbRetry +} + type YGGDResponse struct { // StatusCode response StatusCode int diff --git a/internal/registration/registration_test.go b/internal/registration/registration_test.go index a4bddca1..d570848e 100644 --- a/internal/registration/registration_test.go +++ b/internal/registration/registration_test.go @@ -305,6 +305,32 @@ var _ = Describe("Registration", func() { Eventually(reg.IsRegistered, "5s").Should(BeTrue()) }) + It("Try to re-register exponetial", func() { + // given + reg, err := registration.NewRegistration(deviceID, hw, dispatcherMock, configManager, wkManager) + Expect(err).NotTo(HaveOccurred()) + + msgResponse := getYggdrasilResponse(models.RegistrationResponse{ + Certificate: string(clientCertPem), + }) + + reg.RetryAfter = 1 + + // then + dispatcherMock.EXPECT().Send(gomock.Any(), gomock.Any()).Return( + nil, fmt.Errorf("failed")).Times(3) + dispatcherMock.EXPECT(). + Send(gomock.Any(), RegistrationMatcher()). + Return(&pb.Response{Response: msgResponse}, nil). + Times(1) + + // when + reg.RegisterDevice() + + // then + Eventually(reg.IsRegistered, "8s").Should(BeTrue()) + Eventually(reg.NbRetry, "8s").Should(Equal(3)) + }) }) Context("Deregister", func() {