diff --git a/pkg/services/anonymous/anonimpl/anonstore/database.go b/pkg/services/anonymous/anonimpl/anonstore/database.go index 0db2906709f..688409a0ab2 100644 --- a/pkg/services/anonymous/anonimpl/anonstore/database.go +++ b/pkg/services/anonymous/anonimpl/anonstore/database.go @@ -145,11 +145,17 @@ func (s *AnonDBStore) CreateOrUpdateDevice(ctx context.Context, device *Device) } } + // If CreatedAt time is not set (i.e. it's zero), and we end up creating the device, use current time as creation time. + // Spanner converts zero time to NULL, but CreatedAt is not nullable, so this helps to fix that problem too. + created := device.CreatedAt + if created.IsZero() { + created = time.Now() + } if s.sqlStore.GetDBType() == migrator.Spanner { - return s.insertIntoSpanner(ctx, device) + return s.insertIntoSpanner(ctx, device, created) } - args := []any{device.DeviceID, device.ClientIP, device.UserAgent, device.CreatedAt.UTC(), device.UpdatedAt.UTC()} + args := []any{device.DeviceID, device.ClientIP, device.UserAgent, created.UTC(), device.UpdatedAt.UTC()} switch s.sqlStore.GetDBType() { case migrator.Postgres: query = `INSERT INTO anon_device (device_id, client_ip, user_agent, created_at, updated_at) @@ -188,7 +194,7 @@ func (s *AnonDBStore) CreateOrUpdateDevice(ctx context.Context, device *Device) // In Spanner INSERT OR UPDATE only works when conflict is on primary key. However here we expect conflict on non-PK // column "device_id", so we need to use a transaction instead. -func (s *AnonDBStore) insertIntoSpanner(ctx context.Context, dev *Device) error { +func (s *AnonDBStore) insertIntoSpanner(ctx context.Context, dev *Device, created time.Time) error { return s.sqlStore.WithTransactionalDbSession(ctx, func(dbSession *sqlstore.DBSession) error { prev := &Device{DeviceID: dev.DeviceID} ok, err := dbSession.Table(tableName).Get(prev) @@ -196,7 +202,11 @@ func (s *AnonDBStore) insertIntoSpanner(ctx context.Context, dev *Device) error return err } if !ok { - _, err = dbSession.Table(tableName).Insert(dev) + // Don't call Insert(dev) directly, because successful Insert modifies dev.DeviceID, + // but we don't do the same for other databases. That's why we create a copy. + devCopy := *dev + devCopy.CreatedAt = created + _, err = dbSession.Table(tableName).Insert(devCopy) return err }