package cache import ( "encoding/json" "github.com/fiskerinc/cloud-services/pkg/common" "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/fiskerinc/cloud-services/pkg/redis" r "github.com/gomodule/redigo/redis" "github.com/pkg/errors" ) func RetrieveFileEncryptionParams(client redis.Client, db queries.FileKeysInterface, fileids []string) ([]common.FileKeyResponse, error) { result, err := retrieveFileEncryptionParamsRedis(client, fileids) if err != nil && !errors.Is(err, redis.ErrNilObject) { return nil, err } dbFileIDs := missingFileIDs(result, fileids) if len(dbFileIDs) > 0 { rows, err := retrieveFileEncryptionParamsDB(db, dbFileIDs) if err != nil { return nil, err } err = cacheFileEncryptionParamsRedis(client, rows, redisObjectExpireDay) if err != nil { return nil, err } result = append(rows, result...) } return result, nil } func retrieveFileEncryptionParamsRedis(client redis.Client, fileids []string) ([]common.FileKeyResponse, error) { keys := make([]string, len(fileids)) for i, fileid := range fileids { keys[i] = redis.FileIDEncryptionParamsKey(fileid) } if len(keys) == 0 { return []common.FileKeyResponse{}, nil } values, err := client.GetMulti(keys) if err != nil { return nil, err } result, err := getFileKeyResponses(values) return result, err } func getFileKeyResponses(values []interface{}) ([]common.FileKeyResponse, error) { result := []common.FileKeyResponse{} for _, value := range values { if value == nil { continue } file := common.FileKeyResponse{} data, err := r.Bytes(value, nil) if err != nil { file.Error = err.Error() return result, errors.WithStack(err) } err = json.Unmarshal(data, &file) if err != nil { return result, errors.WithStack(err) } result = append(result, file) } return result, nil } func missingFileIDs(items []common.FileKeyResponse, fileids []string) []string { result := []string{} hash := map[string]bool{} for _, item := range items { if item.FileID != "" { hash[item.FileID] = true } } for _, fileid := range fileids { if !hash[fileid] { result = append(result, fileid) } } return result } func retrieveFileEncryptionParamsDB(db queries.FileKeysInterface, fileids []string) ([]common.FileKeyResponse, error) { result := make([]common.FileKeyResponse, len(fileids)) hash := map[string]bool{} data, err := db.GetMulti(fileids) if err != nil { return nil, errors.WithStack(err) } for i, file := range data { result[i].Apply(&file) hash[file.FileID] = true } if len(fileids) != len(data) { starting := len(data) current := 0 for _, fileid := range fileids { if !hash[fileid] { logger.Warn().Msgf("file id %s not found", fileid) file := &result[starting+current] file.FileID = fileid file.Error = "not found" current++ } } } return result, nil } func cacheFileEncryptionParamsRedis(client redis.Client, files []common.FileKeyResponse, expire int) error { batch := redis.NewRedisBatchCommands() for _, file := range files { serialized, err := json.Marshal(file) if err != nil { return errors.WithStack(err) } id := redis.FileIDEncryptionParamsKey(file.FileID) batch.Add("SET", id, serialized, "EX", expire) } _, err := client.ExecuteBatch(batch) return errors.WithStack(err) }