Skip to content

[3.0] Stop selecting the dropped time_offset column in CreatePost_Notify - #9322

Merged
Sesquipedalian merged 2 commits into
SimpleMachines:release-3.0from
albertlast:fix/createpost-notify-time-offset
Jul 29, 2026
Merged

[3.0] Stop selecting the dropped time_offset column in CreatePost_Notify#9322
Sesquipedalian merged 2 commits into
SimpleMachines:release-3.0from
albertlast:fix/createpost-notify-time-offset

Conversation

@albertlast

Copy link
Copy Markdown
Collaborator

Note

This change was produced by an LLM. The code, the commit message and this
description were all written by Claude (Anthropic), driven by @albertlast. It has
not yet had human code review.

Everything stated below was verified by actually running it against a PostgreSQL
install of this branch, rather than only reasoned about. Even so, please review it
as untrusted work: the diagnosis may be right while the fix is not what SMF would
prefer stylistically or architecturally.

Description

CreatePost_Notify asks the database for a column that 3.0 removed:

mem.smiley_set, mem.time_format, mem.time_offset, mem.timezone,

time_offset was dropped from the members table by the DropTimeOffset migration,
which converts each member's offset into a timezone value first. User::$time_offset
became a virtual property derived from timezone, and User::setProperties() lists
time_offset among the "Obsolete data. Ignore if present." keys.

So the query fails outright:

ERROR:  column mem.time_offset does not exist

and the task dies on the fetch_assoc() of a false result:

SMF\Db\APIs\PostgreSQL::fetch_assoc(): Argument #1 ($result) must be of type object, false given,
called in /var/www/html/Sources/Tasks/CreatePost_Notify.php on line 195

Two consequences. Nobody watching a board or topic ever receives their notification.
And because the task never returns success it is never removed from
background_tasks, so it is retried on subsequent requests and takes down whichever
page happens to run it — an ordinary board index intermittently returning HTTP 500,
which is a confusing thing to chase, since the request that fails has nothing to do
with the post that queued the task.

This derives the offset from mem.timezone, which the query already selects, the
same way User::$time_offset does. Two smaller things fall out of it:

  • the parsed-message cache is keyed by time zone instead of by offset, which is
    what the key is really trying to distinguish;
  • the old (int) cast is gone. Parser::$time_offset is a float, and the cast
    truncated the offsets of half-hour and quarter-hour zones, so members in, say,
    Asia/Kolkata or Asia/Kathmandu had times in their notification emails shifted
    by up to 45 minutes.

How this was verified

PostgreSQL 17 / PHP 8.4.23, with a member watching a board and three
CreatePost_Notify tasks sitting in the queue from earlier posts.

The baseline was release-3.0 plus the pending fixes #9310#9317, #9319 and #9320.
Nothing in this change depends on those; that was simply the branch in hand.

  • Before: three consecutive page loads → HTTP 500 each, three
    fetch_assoc() errors logged, and all three tasks still queued afterwards,
    ready to break the next request too.
  • After: the same three page loads → HTTP 200, no errors, and all three
    tasks completed and removed from the queue.
  • The notification is genuinely delivered rather than merely not crashing: the
    watching member gets their alert row (content_type = 'board'), which never
    appeared before.

php -l clean.

Relationship to other PRs

Sources/Tasks/CreatePost_Notify.php is not touched by any other open PR, and the
failure is independent of the login and ban work in #9310#9319 — any post that
queues a notification triggers it.

One thing to flag: with the query fixed, the task now runs to completion for the
first time, and three warnings become visible further down that path —
Undefined array key "id_alert" at Sources/Alert.php:641, :658 and :659,
where $props['id_alert'] is read on alert rows that have not been inserted yet.
Warning-level only; the task still completes and the alert is still created. Not
addressed here, but this PR is what makes them reachable, so it seems worth saying.

Issues References (Fixes|Related|Closes)

  1. No existing issue found for this.

albertlast and others added 2 commits July 29, 2026 18:49
The members table lost its time_offset column in 3.0, in the
DropTimeOffset migration, and User::$time_offset became a virtual
property derived from the member's time zone. User::setProperties() even
lists time_offset among the "obsolete data" it ignores.

CreatePost_Notify still asked the database for it:

    mem.smiley_set, mem.time_format, mem.time_offset, mem.timezone,

so the query fails outright with "column mem.time_offset does not
exist", and the task dies on the fetch_assoc() of a false result. The
task is never marked done, so it is retried on later page loads and
takes down whichever request happens to run it:

    SMF\Db\APIs\PostgreSQL::fetch_assoc(): Argument #1 ($result) must be
    of type object, false given

Nobody watching a board or topic receives their notification either.

Derives the offset from mem.timezone, which the query already selects,
the same way User::$time_offset does. Also keys the parsed-message cache
by time zone rather than by offset, and drops an (int) cast that
truncated the offsets of half-hour and quarter-hour time zones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PHP-CS-Fixer sorts class members, and a static method does not belong in
the non-static "Internal methods" group. No change to the code itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants