From 259fa1ffac60615a56578537f0d3236fa3a2154c Mon Sep 17 00:00:00 2001 From: tobi Date: Sun, 5 Oct 2025 13:33:16 +0200 Subject: [PATCH 1/2] [bugfix] Update interaction policies of freshly dereffed statuses if different from last deref (#4474) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. This pull request adds a check to see whether interaction policy on a refreshed status is different from the interaction policy set on that status before, and updates the status with the new policy if it's changed. Should fix a pesky issue where folks on v0.19.2 and above still can't interact with statuses they dereferenced before updating. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [ ] I/we have made any necessary changes to documentation. - [ ] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4474 Reviewed-by: kim Co-authored-by: tobi Co-committed-by: tobi --- internal/federation/dereferencing/status.go | 14 ++++ internal/gtsmodel/interactionpolicy.go | 73 +++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go index fffaa88a6..3fa5ac787 100644 --- a/internal/federation/dereferencing/status.go +++ b/internal/federation/dereferencing/status.go @@ -608,6 +608,9 @@ func (d *Dereferencer) enrichStatus( return nil, nil, gtserror.Newf("error populating emojis for status %s: %w", uri, err) } + // Check if interaction policy has changed between status and latestStatus. + interactionPolicyChanged := status.InteractionPolicy.DifferentFrom(latestStatus.InteractionPolicy) + if isNew { // Simplest case, insert this new remote status into the database. if err := d.state.DB.PutStatus(ctx, latestStatus); err != nil { @@ -625,6 +628,7 @@ func (d *Dereferencer) enrichStatus( tagsChanged, mediaChanged, emojiChanged, + interactionPolicyChanged, ) if err != nil { return nil, nil, gtserror.Newf("error handling edit for status %s: %w", uri, err) @@ -1057,6 +1061,7 @@ func (d *Dereferencer) handleStatusEdit( tagsChanged bool, mediaChanged bool, emojiChanged bool, + interactionPolicyChanged bool, ) ( cols []string, err error, @@ -1141,6 +1146,15 @@ func (d *Dereferencer) handleStatusEdit( // been previously populated properly. } + if interactionPolicyChanged { + // Interaction policy changed. + cols = append(cols, "interaction_policy") + + // Int pol changed doesn't necessarily + // indicate an edit, it may just not have + // been previously populated properly. + } + if edited { // Get previous-most-recent modified time, // which will be this edit's creation time. diff --git a/internal/gtsmodel/interactionpolicy.go b/internal/gtsmodel/interactionpolicy.go index 0e248e49e..4fa5f4d8e 100644 --- a/internal/gtsmodel/interactionpolicy.go +++ b/internal/gtsmodel/interactionpolicy.go @@ -17,6 +17,8 @@ package gtsmodel +import "slices" + // A policy URI is GoToSocial's internal representation of // one ActivityPub URI for an Actor or a Collection of Actors, // specific to the domain of enforcing interaction policies. @@ -232,6 +234,39 @@ type PolicyRules struct { ManualApproval PolicyValues `json:"WithApproval,omitempty"` } +// DifferentFrom returns true if pr1 and pr2 +// are not equal in terms of nilness or content. +func (pr1 *PolicyRules) DifferentFrom(pr2 *PolicyRules) bool { + // If one PolicyRules is nil and + // the other isn't, they're different. + if pr1 == nil && pr2 != nil || + pr1 != nil && pr2 == nil { + return true + } + + // Check if AutomaticApproval + // differs between the two. + if slices.Compare( + pr1.AutomaticApproval, + pr2.AutomaticApproval, + ) != 0 { + return true + } + + // Check if ManualApproval + // differs between the two. + if slices.Compare( + pr1.ManualApproval, + pr2.ManualApproval, + ) != 0 { + return true + } + + // They're the + // same picture. + return false +} + // Returns the default interaction policy // for the given visibility level. func DefaultInteractionPolicyFor(v Visibility) *InteractionPolicy { @@ -422,3 +457,41 @@ func DefaultInteractionPolicyDirect() *InteractionPolicy { *c = *defaultPolicyDirect return c } + +// DifferentFrom returns true if p1 and p2 are different. +func (ip1 *InteractionPolicy) DifferentFrom(ip2 *InteractionPolicy) bool { + // If one policy is null and the + // other isn't, they're different. + if ip1 == nil && ip2 != nil || + ip1 != nil && ip2 == nil { + return true + } + + // If they're both nil we don't + // need to check anything else. + if ip1 == nil && ip2 == nil { + return false + } + + // If CanLike differs from one policy + // to the next, they're different. + if ip1.CanLike.DifferentFrom(ip2.CanLike) { + return true + } + + // If CanReply differs from one policy + // to the next, they're different. + if ip1.CanReply.DifferentFrom(ip2.CanReply) { + return true + } + + // If CanAnnounce differs from one policy + // to the next, they're different. + if ip1.CanAnnounce.DifferentFrom(ip2.CanAnnounce) { + return true + } + + // Looks the + // same chief. + return false +} From 5b956369934e448db285eb5f3e890f280cfa87de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Franke?= Date: Sun, 5 Oct 2025 14:43:09 +0200 Subject: [PATCH 2/2] [docs] Add db migration tip for slow hardware instances. (#4457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds a new section to the documentation to contain workarounds for running GtS on slow hardware. Right now it only contains a procedure on how to run migrations on a different database instance in case the original database is too slow to finish a database migration in a timely manner. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4457 Co-authored-by: Daniël Franke Co-committed-by: Daniël Franke --- docs/admin/slow_hardware.md | 43 +++++++++++++++++++++++++++++++++++++ mkdocs.yml | 3 ++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 docs/admin/slow_hardware.md diff --git a/docs/admin/slow_hardware.md b/docs/admin/slow_hardware.md new file mode 100644 index 000000000..8ba5a6f86 --- /dev/null +++ b/docs/admin/slow_hardware.md @@ -0,0 +1,43 @@ +# Managing GtS on slow hardware + +While GoToSocial runs great on lower-end hardware, some operations are not practical on it, especially +instances with the database on slow storage (think anything that is not an SSD). This document +offers some suggestions on how to work around common issues when running GtS on slow hardware. + +## Running database migrations on a different machine + +Sometimes a database migration will need to do operations that are taxing on the database's storage. +These operations can take days if the database resides on a hard disk or SD card. If your +database is on slow storage, it can save a lot of time to follow the following procedure: + +!!! danger + + It might seem tempting to keep GtS running while you run the migrations on another machine, but + doing this will lead to all the posts that are received during the migration post disappearing + once the migrated database is re-imported. + + 1. Shut down GtS + 2. Take a [backup](backup_and_restore.md#what-to-backup-database) of the database + 3. Import the database on faster hardware + 4. Run the GtS migration on the faster hardware + 5. Take a backup of the resultant database + 6. Import the resultant backup and overwrite the old database + 7. Start GtS with the new version + +### Running GtS migrations separately + +After you import the database on the faster hardware, you can run the migration without starting +GtS by downloading the *target* GtS version from the [releases](https://codeberg.org/superseriousbusiness/gotosocial/releases) page. +For instance, if you are running `v0.19.2` and you want to upgrade to `v0.20.0-rc1`, download the +latter version. Once you have the binary, set it to executable by running `chmod u+x /path/to/gotosocial`. Afterwards, copy the configuration of the original server, and alter +it with the location of the new database. We copy the configuration in case variables like +the hostname is used in the migration, we want to keep that consistent. +Once everything is in place, you can run the migration like this: + +```sh +$ /path/to/gotosocial --config-path /path/to/config migrations run +``` + +This will run all the migrations, just like GtS would if it was started normally. Once this is done +you can copy the result to the original instance and start the new GtS version there as well, which +will see that everything is migrated and that there's nothing to do except run as expected. diff --git a/mkdocs.yml b/mkdocs.yml index 5c33d764e..0d5528080 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -38,7 +38,7 @@ plugins: extra: alternate: - name: English - link: /en/ + link: /en/ lang: en - name: 中文 link: /zh-cn/ @@ -149,6 +149,7 @@ nav: - "admin/spam.md" - "admin/database_maintenance.md" - "admin/themes.md" + - "admin/slow_hardware.md" - "Federation": - "federation/index.md" - "federation/http_signatures.md"