diff options
Diffstat (limited to 'internal/db/bundb/migrations/20220214175650_media_cleanup.go')
-rw-r--r-- | internal/db/bundb/migrations/20220214175650_media_cleanup.go | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/internal/db/bundb/migrations/20220214175650_media_cleanup.go b/internal/db/bundb/migrations/20220214175650_media_cleanup.go new file mode 100644 index 000000000..427f3bb9e --- /dev/null +++ b/internal/db/bundb/migrations/20220214175650_media_cleanup.go @@ -0,0 +1,172 @@ +/* + GoToSocial + Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package migrations + +import ( + "context" + "database/sql" + "time" + + previousgtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations/20211113114307_init" + newgtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations/20220214175650_media_cleanup" + "github.com/uptrace/bun" +) + +func init() { + const batchSize = 100 + up := func(ctx context.Context, db *bun.DB) error { + // we need to migrate media attachments into a new table + // see section 6 here: https://www.sqlite.org/lang_altertable.html + + return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { + // create the new media attachments table + if _, err := tx. + NewCreateTable(). + ModelTableExpr("new_media_attachments"). + Model(&newgtsmodel.MediaAttachment{}). + IfNotExists(). + Exec(ctx); err != nil { + return err + } + + offset := time.Now() + // migrate existing media attachments into new table + migrateLoop: + for { + oldAttachments := []*previousgtsmodel.MediaAttachment{} + err := tx. + NewSelect(). + Model(&oldAttachments). + // subtract a millisecond from the offset just to make sure we're not getting double entries (this happens sometimes) + Where("media_attachment.created_at < ?", offset.Add(-1*time.Millisecond)). + Order("media_attachment.created_at DESC"). + Limit(batchSize). + Scan(ctx) + if err != nil && err != sql.ErrNoRows { + // there's been a real error + return err + } + + if err == sql.ErrNoRows || len(oldAttachments) == 0 { + // we're finished migrating + break migrateLoop + } + + // update the offset to the createdAt time of the oldest media attachment in the slice + offset = oldAttachments[len(oldAttachments)-1].CreatedAt + + // for every old attachment, we need to make a new attachment out of it by taking the same values + newAttachments := []*newgtsmodel.MediaAttachment{} + for _, old := range oldAttachments { + new := &newgtsmodel.MediaAttachment{ + ID: old.ID, + CreatedAt: old.CreatedAt, + UpdatedAt: old.UpdatedAt, + StatusID: old.StatusID, + URL: old.URL, + RemoteURL: old.RemoteURL, + Type: newgtsmodel.FileType(old.Type), + FileMeta: newgtsmodel.FileMeta{ + Original: newgtsmodel.Original{ + Width: old.FileMeta.Original.Width, + Height: old.FileMeta.Original.Height, + Size: old.FileMeta.Original.Size, + Aspect: old.FileMeta.Original.Aspect, + }, + Small: newgtsmodel.Small{ + Width: old.FileMeta.Small.Width, + Height: old.FileMeta.Small.Height, + Size: old.FileMeta.Small.Size, + Aspect: old.FileMeta.Small.Aspect, + }, + Focus: newgtsmodel.Focus{ + X: old.FileMeta.Focus.X, + Y: old.FileMeta.Focus.Y, + }, + }, + AccountID: old.AccountID, + Description: old.Description, + ScheduledStatusID: old.ScheduledStatusID, + Blurhash: old.Blurhash, + Processing: newgtsmodel.ProcessingStatus(old.Processing), + File: newgtsmodel.File{ + Path: old.File.Path, + ContentType: old.File.ContentType, + FileSize: old.File.FileSize, + UpdatedAt: old.File.UpdatedAt, + }, + Thumbnail: newgtsmodel.Thumbnail{ + Path: old.Thumbnail.Path, + ContentType: old.Thumbnail.ContentType, + FileSize: old.Thumbnail.FileSize, + UpdatedAt: old.Thumbnail.UpdatedAt, + URL: old.Thumbnail.URL, + RemoteURL: old.Thumbnail.RemoteURL, + }, + Avatar: old.Avatar, + Header: old.Header, + Cached: true, + } + newAttachments = append(newAttachments, new) + } + + // insert this batch of new attachments, and then continue the loop + if _, err := tx. + NewInsert(). + Model(&newAttachments). + ModelTableExpr("new_media_attachments"). + Exec(ctx); err != nil { + return err + } + } + + // we have all the data we need from the old table, so we can safely drop it now + if _, err := tx.NewDropTable().Model(&previousgtsmodel.MediaAttachment{}).Exec(ctx); err != nil { + return err + } + + // rename the new table to the same name as the old table was + if _, err := tx.QueryContext(ctx, "ALTER TABLE new_media_attachments RENAME TO media_attachments;"); err != nil { + return err + } + + // add an index to the new table + if _, err := tx. + NewCreateIndex(). + Model(&newgtsmodel.MediaAttachment{}). + Index("media_attachments_id_idx"). + Column("id"). + Exec(ctx); err != nil { + return err + } + + return nil + }) + } + + down := func(ctx context.Context, db *bun.DB) error { + return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { + return nil + }) + } + + if err := Migrations.Register(up, down); err != nil { + panic(err) + } +} |