diff --git a/pkg/api/user_app_cloud_settings.go b/pkg/api/user_app_cloud_settings.go index 3cb38a74..bf751e46 100644 --- a/pkg/api/user_app_cloud_settings.go +++ b/pkg/api/user_app_cloud_settings.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "time" "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/errs" @@ -74,115 +75,129 @@ func (a *UserApplicationCloudSettingsApi) ApplicationSettingsUpdateHandler(c *co return false, errs.ErrNotPermittedToPerformThisAction } - userApplicationCloudSettings, err := a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, uid) + var userApplicationCloudSettings *models.UserApplicationCloudSetting - if err != nil { - log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] failed to get latest user application cloud settings for user \"uid:%d\", because %s", uid, err.Error()) - return false, errs.Or(err, errs.ErrOperationFailed) - } + // Retry up to 3 times + for i := 0; i < 3; i++ { + userApplicationCloudSettings, err = a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, uid) - oldApplicationCloudSettingsMap := make(map[string]models.ApplicationCloudSetting) - - if userApplicationCloudSettings != nil { - for _, setting := range userApplicationCloudSettings.Settings { - oldApplicationCloudSettingsMap[setting.SettingKey] = setting + if err != nil { + log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] failed to get latest user application cloud settings for user \"uid:%d\" (try count %d), because %s", uid, i+1, err.Error()) + return false, errs.Or(err, errs.ErrOperationFailed) } - } - // Check if the full update settings are the same as the existing settings - if userAppCloudSettingUpdateReq.FullUpdate { - if len(userAppCloudSettingUpdateReq.Settings) == len(oldApplicationCloudSettingsMap) { - needUpdate := false + oldApplicationCloudSettingsMap := make(map[string]models.ApplicationCloudSetting) + lastUpdateTime := int64(0) + + if userApplicationCloudSettings != nil { + for _, setting := range userApplicationCloudSettings.Settings { + oldApplicationCloudSettingsMap[setting.SettingKey] = setting + } + + lastUpdateTime = userApplicationCloudSettings.UpdatedUnixTime + } + + // Check if the full update settings are the same as the existing settings + if userAppCloudSettingUpdateReq.FullUpdate { + if len(userAppCloudSettingUpdateReq.Settings) == len(oldApplicationCloudSettingsMap) { + needUpdate := false + + for _, setting := range userAppCloudSettingUpdateReq.Settings { + oldSetting, exists := oldApplicationCloudSettingsMap[setting.SettingKey] + + if !exists || oldSetting.SettingValue != setting.SettingValue { + needUpdate = true + break + } + } + + if !needUpdate { + return false, errs.ErrNothingWillBeUpdated + } + } + } else { // Check if the partial update settings are the same as the existing settings or the settings to update are not set to sync + needUpdate := true for _, setting := range userAppCloudSettingUpdateReq.Settings { - oldSetting, exists := oldApplicationCloudSettingsMap[setting.SettingKey] + cloudSetting, exists := oldApplicationCloudSettingsMap[setting.SettingKey] - if !exists || oldSetting.SettingValue != setting.SettingValue { - needUpdate = true - break + if !exists { + needUpdate = false + log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" is not set to sync (try count %d)", setting.SettingKey, i+1) + } else if cloudSetting.SettingValue == setting.SettingValue { + needUpdate = false + log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" value \"%s\" is not changed, no need to update (try count %d)", setting.SettingKey, setting.SettingValue, i+1) } } if !needUpdate { - return false, errs.ErrNothingWillBeUpdated + log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] no user application cloud settings need to update for user \"uid:%d\" (try count %d)", uid, i+1) + return true, nil + } + } + + newApplicationCloudSettingsMap := make(map[string]models.ApplicationCloudSetting) + var newApplicationCloudSettingSlice models.ApplicationCloudSettingSlice + + if userAppCloudSettingUpdateReq.FullUpdate { + log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user \"uid:%d\" application cloud settings force update, will overwrite all existing settings (try count %d)", uid, i+1) + } else { + if len(oldApplicationCloudSettingsMap) > 0 { + log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user \"uid:%d\" application cloud settings exists, try to merge it with request settings (try count %d)", uid, i+1) + newApplicationCloudSettingsMap = oldApplicationCloudSettingsMap } } - } else { // Check if the partial update settings are the same as the existing settings or the settings to update are not set to sync - needUpdate := true for _, setting := range userAppCloudSettingUpdateReq.Settings { - cloudSetting, exists := oldApplicationCloudSettingsMap[setting.SettingKey] + newApplicationCloudSettingsMap[setting.SettingKey] = setting + } + + for settingKey, setting := range newApplicationCloudSettingsMap { + settingType, exists := models.ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES[settingKey] if !exists { - needUpdate = false - log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" is not set to sync", setting.SettingKey) - } else if cloudSetting.SettingValue == setting.SettingValue { - needUpdate = false - log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" value \"%s\" is not changed, no need to update", setting.SettingKey, setting.SettingValue) - } - } - - if !needUpdate { - log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] no user application cloud settings need to update for user \"uid:%d\"", uid) - return false, nil - } - } - - newApplicationCloudSettingsMap := make(map[string]models.ApplicationCloudSetting) - var newApplicationCloudSettingSlice models.ApplicationCloudSettingSlice - - if userAppCloudSettingUpdateReq.FullUpdate { - log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user \"uid:%d\" application cloud settings force update, will overwrite all existing settings", uid) - } else { - if len(oldApplicationCloudSettingsMap) > 0 { - log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user \"uid:%d\" application cloud settings exists, try to merge it with request settings", uid) - newApplicationCloudSettingsMap = oldApplicationCloudSettingsMap - } - } - - for _, setting := range userAppCloudSettingUpdateReq.Settings { - newApplicationCloudSettingsMap[setting.SettingKey] = setting - } - - for settingKey, setting := range newApplicationCloudSettingsMap { - settingType, exists := models.ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES[settingKey] - - if !exists { - log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" is not supported to sync", settingKey) - continue - } - - if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_STRING { - // Do Nothing - } else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER { - _, err := utils.StringToFloat64(setting.SettingValue) - - if err != nil { - log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid number value \"%s\"", settingKey, setting.SettingValue) + log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" is not supported to sync (try count %d)", settingKey, i+1) continue } - } else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN { - if setting.SettingValue != "true" && setting.SettingValue != "false" { - log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid boolean value \"%s\"", settingKey, setting.SettingValue) - continue - } - } else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_STRING_BOOLEAN_MAP { - var settingValueMap map[string]bool - err := json.Unmarshal([]byte(setting.SettingValue), &settingValueMap) - if err != nil { - log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid map value \"%s\", because %s", settingKey, setting.SettingValue, err.Error()) + if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_STRING { + // Do Nothing + } else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER { + _, err := utils.StringToFloat64(setting.SettingValue) + + if err != nil { + log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid number value \"%s\" (try count %d)", settingKey, setting.SettingValue, i+1) + continue + } + } else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN { + if setting.SettingValue != "true" && setting.SettingValue != "false" { + log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid boolean value \"%s\" (try count %d)", settingKey, setting.SettingValue, i+1) + continue + } + } else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_STRING_BOOLEAN_MAP { + var settingValueMap map[string]bool + err := json.Unmarshal([]byte(setting.SettingValue), &settingValueMap) + + if err != nil { + log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid map value \"%s\" (try count %d), because %s", settingKey, setting.SettingValue, i+1, err.Error()) + continue + } + } else { + log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has unknown type \"%s\" (try count %d)", settingKey, settingType, i+1) continue } - } else { - log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has unknown type \"%s\"", settingKey, settingType) - continue + + newApplicationCloudSettingSlice = append(newApplicationCloudSettingSlice, setting) } - newApplicationCloudSettingSlice = append(newApplicationCloudSettingSlice, setting) - } + err = a.userAppCloudSettings.UpdateUserApplicationCloudSettings(c, uid, newApplicationCloudSettingSlice, userAppCloudSettingUpdateReq.FullUpdate, lastUpdateTime) - err = a.userAppCloudSettings.UpdateUserApplicationCloudSettings(c, uid, newApplicationCloudSettingSlice) + if err == nil { + break + } + + time.Sleep(100 * time.Millisecond) // Wait for 100 milliseconds before retrying + } if err != nil { log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] failed to update user application cloud settings for user \"uid:%d\", because %s", uid, err.Error()) diff --git a/pkg/services/user_app_cloud_settings.go b/pkg/services/user_app_cloud_settings.go index 28068c4c..ff9920bb 100644 --- a/pkg/services/user_app_cloud_settings.go +++ b/pkg/services/user_app_cloud_settings.go @@ -9,6 +9,7 @@ import ( "github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/datastore" "github.com/mayswind/ezbookkeeping/pkg/errs" + "github.com/mayswind/ezbookkeeping/pkg/log" "github.com/mayswind/ezbookkeeping/pkg/models" ) @@ -45,7 +46,7 @@ func (s *UserApplicationCloudSettingsService) GetUserApplicationCloudSettingsByU } // UpdateUserApplicationCloudSettings updates user application cloud settings -func (s *UserApplicationCloudSettingsService) UpdateUserApplicationCloudSettings(c core.Context, uid int64, settings models.ApplicationCloudSettingSlice) error { +func (s *UserApplicationCloudSettingsService) UpdateUserApplicationCloudSettings(c core.Context, uid int64, settings models.ApplicationCloudSettingSlice, forceUpdate bool, lastUpdateTime int64) error { if uid <= 0 { return errs.ErrUserIdInvalid } @@ -65,14 +66,21 @@ func (s *UserApplicationCloudSettingsService) UpdateUserApplicationCloudSettings return err } + updatedRows := int64(0) + if !exists { - _, err = sess.Insert(userApplicationCloudSetting) + updatedRows, err = sess.Insert(userApplicationCloudSetting) + } else if forceUpdate || lastUpdateTime <= 0 { + updatedRows, err = sess.ID(uid).Cols("settings", "updated_unix_time").Update(userApplicationCloudSetting) } else { - _, err = sess.ID(uid).Cols("settings", "updated_unix_time").Update(userApplicationCloudSetting) + updatedRows, err = sess.ID(uid).Cols("settings", "updated_unix_time").Where("updated_unix_time=?", lastUpdateTime).Update(userApplicationCloudSetting) } if err != nil { return err + } else if updatedRows < 1 { + log.Errorf(c, "[user_app_cloud_settings.UpdateUserApplicationCloudSettings] failed to update user application cloud settings") + return errs.ErrDatabaseOperationFailed } return nil