package cache import ( "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" "github.com/pkg/errors" ) func NewDriversCache(redisClient redis.ClientPoolInterface, cars queries.CarsInterface) *DriversCache { return &DriversCache{ redisClientPool: redisClient, cars: cars, } } type DriversCache struct { redisClientPool redis.ClientPoolInterface cars queries.CarsInterface } func (dc *DriversCache) RedisClientPool() redis.ClientPoolInterface { return dc.redisClientPool } func (dc *DriversCache) Cars() queries.CarsInterface { return dc.cars } func (dc *DriversCache) hasCachedNoDrivers(drivers []string) bool { // Redis will return []string{""} for no drivers return len(drivers) == 1 && len(drivers[0]) == 0 } func (dc *DriversCache) cacheDrivers(key string, drivers []string) error { client := dc.redisClientPool.GetFromPool() defer client.Close() // cache driver IDs if len(drivers) > 0 { return client.NewSet(key, drivers, redisObjectExpire) } // Redis will not take an empty array as an arg return client.NewSet(key, nil, redisObjectExpire) } // RetrieveDriverIDs retrieves IDs from redis or from DB and proceeds to cache both the drivers and IDs // redis keys: // // car::drivers func (dc *DriversCache) RetrieveDriverIDs(vin string) ([]string, error) { var driverIDs []string driverIDsKey := redis.CarToAllDriversKey(vin) // retrieve IDs from redis client := dc.redisClientPool.GetFromPool() err := client.GetSet(driverIDsKey, &driverIDs) if err != nil { logger.Warn().Err(err).Send() } client.Close() if dc.hasCachedNoDrivers(driverIDs) { return []string{}, nil } if len(driverIDs) > 0 { return driverIDs, nil } // if IDs not present in redis perform DB lookup var drivers []common.CarToDriver drivers, err = dc.cars.GetDrivers(vin) if err != nil { return nil, err } for _, driver := range drivers { driverIDs = append(driverIDs, driver.DriverID) } err = dc.cacheDrivers(driverIDsKey, driverIDs) if err != nil { return driverIDs, err } return driverIDs, nil } // RetrieveDriverIDsAsSet retrieves IDs from redis or from DB and proceeds to cache both the drivers and IDs // redis keys: // // car::drivers func (dc *DriversCache) RetrieveDriverIDsAsSet(vin string) (map[string]struct{}, error) { driverIDs, err := dc.RetrieveDriverIDs(vin) if err != nil { return nil, err } var dIDsSet = make(map[string]struct{}) for _, did := range driverIDs { dIDsSet[did] = struct{}{} } return dIDsSet, nil } func (dc *DriversCache) IsDriverOfVIN(vin string, driverid string) (bool, error) { ids, err := dc.RetrieveDriverIDs(vin) if err != nil { return false, err } for _, id := range ids { if id == driverid { return true, nil } } return false, dc.NotDriverError(vin, driverid) } // Add driver to database and cache func (dc *DriversCache) AddDriver(car *common.Car, driver *common.Driver, role string) (*common.CarToDriver, error) { relation, err := dc.cars.AddDriver(car, driver, role) if err != nil { return nil, err } driverIDsKey := redis.CarToAllDriversKey(car.VIN) client := dc.redisClientPool.GetFromPool() defer client.Close() client.AddToSet(driverIDsKey, driver.ID, redisObjectExpire) return relation, nil } func (dc DriversCache) NotDriverError(vin string, driverid string) error { return errors.Errorf("id %s is not a driver for vin %v", driverid, vin) }