0.1.52 Gateway
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeviceActivityRetention is how long the per-day marks are kept. They are not history
|
||||
// anybody reads — the notification they produced is the record, and that is itself pruned
|
||||
// at thirty days — so this only has to be long enough that a clock correction or a
|
||||
// database restored from a backup cannot make yesterday look like a day that never
|
||||
// happened.
|
||||
const DeviceActivityRetention = 90 * 24 * time.Hour
|
||||
|
||||
// MarkDeviceDay records that a television was in use on a household-local day, and
|
||||
// reports whether this is the first time it has been seen today.
|
||||
//
|
||||
// The answer comes from the insert rather than from a read followed by a write, because
|
||||
// every set in the house can reach this at once and two of them racing on the same row
|
||||
// must not both be told they were first. `ON CONFLICT DO NOTHING` makes the primary key
|
||||
// the arbiter: exactly one insert affects a row.
|
||||
//
|
||||
// It is keyed on the viewer as well as the television because a household set that two
|
||||
// people sign into is two people opening Memby, and an operator reading the feed wants to
|
||||
// know which of them it was.
|
||||
func (s *Store) MarkDeviceDay(
|
||||
ctx context.Context, deviceID, userID string, day time.Time,
|
||||
) (bool, error) {
|
||||
if deviceID == "" {
|
||||
return false, nil
|
||||
}
|
||||
tag, err := s.pool.Exec(ctx, `
|
||||
INSERT INTO device_activity_days (device_id, emby_user_id, day)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (device_id, emby_user_id, day) DO NOTHING`,
|
||||
deviceID, userID, day.Format("2006-01-02"))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("store: mark device day: %w", err)
|
||||
}
|
||||
return tag.RowsAffected() > 0, nil
|
||||
}
|
||||
|
||||
// PruneDeviceActivityDays drops marks older than the retention window.
|
||||
func (s *Store) PruneDeviceActivityDays(
|
||||
ctx context.Context, retention time.Duration,
|
||||
) (int64, error) {
|
||||
if retention <= 0 {
|
||||
retention = DeviceActivityRetention
|
||||
}
|
||||
tag, err := s.pool.Exec(ctx,
|
||||
`DELETE FROM device_activity_days WHERE first_seen_at < now() - $1::interval`,
|
||||
fmt.Sprintf("%d seconds", int64(retention.Seconds())))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("store: prune device activity days: %w", err)
|
||||
}
|
||||
return tag.RowsAffected(), nil
|
||||
}
|
||||
|
||||
// DeleteDeviceActivityDays retires a television's marks along with the television, the
|
||||
// way its build history is retired: a set that is gone must not be able to announce a
|
||||
// first use it can no longer have.
|
||||
func (s *Store) DeleteDeviceActivityDays(ctx context.Context, deviceIDs ...string) error {
|
||||
if len(deviceIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := s.pool.Exec(ctx,
|
||||
`DELETE FROM device_activity_days WHERE device_id = ANY($1::text[])`, deviceIDs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: delete device activity days: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user