package sensors import ( "log/slog" "sync" "time" "github.com/jackc/pgx/v5/pgxpool" ) type Repository interface { RegisterSensor(s Sensor) error UpdateSensorConfig(s Sensor) error ReadSensor(id int) (Sensor, error) ReadSensorValues(id int, from, to time.Time) ([]SensorData, error) ReadAllSensors() ([]Sensor, error) } type pgxRepo struct { pool *pgxpool.Pool } func newPGXRepo(pool *pgxpool.Pool) Repository { return &pgxRepo{ pool: pool, } } func (p *pgxRepo) ReadSensor(id int) (Sensor, error) { panic("unimplemented") } func (p *pgxRepo) UpdateSensorConfig(s Sensor) error { panic("unimplemented") } func (p *pgxRepo) RegisterSensor(s Sensor) error { panic("unimplemented") } func (p *pgxRepo) ReadSensorValues(id int, from time.Time, to time.Time) ([]SensorData, error) { panic("unimplemented") } func (p *pgxRepo) ReadAllSensors() ([]Sensor, error) { panic("unimplemented") } type inMemory struct { sensors map[string]*Sensor mu *sync.Mutex } func newInMemoryRepo() Repository { return &inMemory{ sensors: make(map[string]*Sensor), } } func (i *inMemory) RegisterSensor(s Sensor) error { panic("unimplemented") } func (i *inMemory) UpdateSensorConfig(s Sensor) error { panic("unimplemented") } func (i *inMemory) ReadSensor(id int) (Sensor, error) { panic("unimplemented") } func (i *inMemory) ReadSensorValues(id int, from time.Time, to time.Time) ([]SensorData, error) { // holds only last 100 values for every sensor panic("unimplemented") } func (i *inMemory) ReadAllSensors() ([]Sensor, error) { panic("unimplemented") } type DecoratorRepo struct { db Repository memory Repository } func NewDecoratorRepo(pool *pgxpool.Pool) Repository { db := newPGXRepo(pool) memory := newInMemoryRepo() sensors, err := db.ReadAllSensors() if err != nil { slog.Error("error warming up cache") } for _, s := range sensors { _ = memory.RegisterSensor(s) } return &DecoratorRepo{ db: db, memory: memory, } } func (d *DecoratorRepo) RegisterSensor(s Sensor) error { if err := d.db.RegisterSensor(s); err != nil { return err } _ = d.memory.RegisterSensor(s) return nil } func (d *DecoratorRepo) UpdateSensorConfig(s Sensor) error { if err := d.db.UpdateSensorConfig(s); err != nil { return err } _ = d.memory.UpdateSensorConfig(s) return nil } func (d *DecoratorRepo) ReadSensor(id int) (Sensor, error) { sensor, err := d.memory.ReadSensor(id) if err == nil { return sensor, nil } return d.db.ReadSensor(id) } func (d *DecoratorRepo) ReadSensorValues(id int, from, to time.Time) ([]SensorData, error) { values, err := d.memory.ReadSensorValues(id, from, to) if err == nil && len(values) > 0 { return values, nil } return d.db.ReadSensorValues(id, from, to) } func (d *DecoratorRepo) ReadAllSensors() ([]Sensor, error) { sensors, err := d.memory.ReadAllSensors() if err == nil && len(sensors) > 0 { return sensors, nil } return d.db.ReadAllSensors() }