Page MenuHomePhabricator

Change username sorting to be alphabetic in Contributions tab
Closed, ResolvedPublic

Description

Background:

Right now, the usernames in the Contributions tab are sorted by user id, which is not intuitive or very useful.

Acceptance Criteria:
  • Given a user is on Special:EventDetails, contributions tab
    • And they sort the table by user
      • Then users should be sorted alphabetically according to their username
  • And if a user is renamed via either UserMerge, Renameuser, or CentralAuth's global rename. [NOTE: Some of these are for single-wiki envs and don't work when the others are installed. Check the docs]
    • Then their username in the table view is also updated
    • And sorting by user uses the new username
  • And if a user is hidden or deleted via CentralAuth, OR if it's suppressed via Special:Block in a single-wiki environment
    • Then their username is removed from the ce_event_contributions DB table
    • And the table view shows "Deleted user" or similar messages, unchanged from before this task
    • And sorting by username treats all deleted/hidden users the same, regardless of their IDs
    • And if the user is later unhidden/undeleted, these changes are reverted

Notes: You will need to apply the schema change manually in your local environment; for example, assuming SQLite:

ALTER TABLE ce_event_contributions ADD COLUMN cec_user_name BLOB DEFAULT NULL;
CREATE INDEX cec_user_id_name ON ce_event_contributions (cec_user_id, cec_user_name);

Then, if you have existing entries and would like to backfill them (instead of truncating the table and regenerating them), you can use the script in P83943.

Event Timeline

ifried renamed this task from Change username sorting to be alphabetic in Contributions tab to Investigation: Change username sorting to be alphabetic in Contributions tab.Sep 18 2025, 4:45 PM
ifried updated the task description. (Show Details)

This issue was previously faced for the participant search in Special:EventDetails. See tasks T308574 and T312645, proposed schema + mention of things to watch our for re renames, and workaround (unfiltered query followed by filtering in PHP code; yuck). That work was eventually declined, but judgind from the above alone it's not clear if there were other reasons. I'm also not sure how complex it might really be dealing with renames. Maybe it was just a case of "non super-simple, the workaround is good enough, let's not bother".

Daimona renamed this task from Investigation: Change username sorting to be alphabetic in Contributions tab to Change username sorting to be alphabetic in Contributions tab.Oct 7 2025, 12:09 PM
Daimona updated the task description. (Show Details)

Change #1194230 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] Update database schema to store usernames associated with contributions

https://gerrit.wikimedia.org/r/1194230

Change #1194242 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] Add username to the event contribution entity layer

https://gerrit.wikimedia.org/r/1194242

Change #1195732 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CentralAuth@master] Add new hooks fired upon account hidden and deleted

https://gerrit.wikimedia.org/r/1195732

Change #1195749 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] Rename UpdateContributionRecordsJob to clarify that it handles page changes

https://gerrit.wikimedia.org/r/1195749

Change #1195748 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] [WIP] Add user update methods to EventContributionStore

https://gerrit.wikimedia.org/r/1195748

Change #1195751 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] [WIP] Add hooks to handle user updates in event contributions

https://gerrit.wikimedia.org/r/1195751

Hi DBA s! Tagging you to request approval for the following schema change, as per https://wikitech.wikimedia.org/wiki/Schema_changes. When approved and merged, I fill file a separate task to get it applied in prod.

ALTERs
ALTER TABLE ce_event_contributions ADD cec_user_name VARBINARY(255) DEFAULT NULL;

CREATE INDEX cec_user_id_name ON ce_event_contributions (cec_user_id, cec_user_name);

See also JSON diff with comments. Note that this is not being done in a backwards-compatible fashion because the table was introduced recently and is unused. We'll unbreak beta manually when the patch is merged.

Change #1195761 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] Check feature flag in ContributionAssociationPageEventIngress

https://gerrit.wikimedia.org/r/1195761

Hi DBA s! Tagging you to request approval for the following schema change, as per https://wikitech.wikimedia.org/wiki/Schema_changes. When approved and merged, I fill file a separate task to get it applied in prod.

ALTERs
ALTER TABLE ce_event_contributions ADD cec_user_name VARBINARY(255) DEFAULT NULL;

CREATE INDEX cec_user_id_name ON ce_event_contributions (cec_user_id, cec_user_name);

See also JSON diff with comments. Note that this is not being done in a backwards-compatible fashion because the table was introduced recently and is unused. We'll unbreak beta manually when the patch is merged.

Hi, my understanding from the ticket description is that you want to order based on user_name so the index seems in the wrong order? It should be first cec_user_name? maybe you're adding the event for example? (cec_event_id, cec_user_name)

Hi DBA s! Tagging you to request approval for the following schema change, as per https://wikitech.wikimedia.org/wiki/Schema_changes. When approved and merged, I fill file a separate task to get it applied in prod.

ALTERs
ALTER TABLE ce_event_contributions ADD cec_user_name VARBINARY(255) DEFAULT NULL;

CREATE INDEX cec_user_id_name ON ce_event_contributions (cec_user_id, cec_user_name);

See also JSON diff with comments. Note that this is not being done in a backwards-compatible fashion because the table was introduced recently and is unused. We'll unbreak beta manually when the patch is merged.

Hi, my understanding from the ticket description is that you want to order based on user_name so the index seems in the wrong order? It should be first cec_user_name? maybe you're adding the event for example? (cec_event_id, cec_user_name)

Apologies, I should've explained this. The index is for handling renames and user deletions/suppressions. For example in the case of renames, we're going to have queries like UPDATE ... SET user_name = 'Y' WHERE user_id = 123 AND user_name != 'Y' (see actual implementation in review), which is what that index covers. For sorting, what we agreed upon in T402659 still holds, i.e., we only filter by event with the index, then let it scan and discard/sort the rest.

Okay then, it sounds good to me.

Change #1196137 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[integration/config@master] Zuul: [mediawiki/extensions/CampaignEvents] Add new dependecies

https://gerrit.wikimedia.org/r/1196137

Change #1196137 merged by jenkins-bot:

[integration/config@master] Zuul: [mediawiki/extensions/CampaignEvents] Add new dependecies

https://gerrit.wikimedia.org/r/1196137

Change #1196180 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[integration/config@master] Zuul: [mediawiki/extensions/CampaignEvents] Add CentralAuth dependency

https://gerrit.wikimedia.org/r/1196180

Change #1196180 merged by jenkins-bot:

[integration/config@master] Zuul: [mediawiki/extensions/CampaignEvents] Add CentralAuth dependency

https://gerrit.wikimedia.org/r/1196180

Mentioned in SAL (#wikimedia-releng) [2025-10-15T13:44:09Z] <James_F> Zuul: [mediawiki/extensions/CampaignEvents] Add CentralAuth dependency, for T404995

Change #1196482 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] Use stored usernames in EventContributionsPager

https://gerrit.wikimedia.org/r/1196482

Change #1195761 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Check feature flag in ContributionAssociationPageEventIngress

https://gerrit.wikimedia.org/r/1195761

Change #1194230 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Update database schema to store usernames associated with contributions

https://gerrit.wikimedia.org/r/1194230

Change #1194242 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Add username to the event contribution entity layer

https://gerrit.wikimedia.org/r/1194242

Change #1195749 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Rename UpdateContributionRecordsJob to clarify that it handles pages

https://gerrit.wikimedia.org/r/1195749

Change #1195748 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Add user update methods to EventContributionStore

https://gerrit.wikimedia.org/r/1195748

Change #1195732 merged by jenkins-bot:

[mediawiki/extensions/CentralAuth@master] Add new hooks fired upon account hidden and deleted

https://gerrit.wikimedia.org/r/1195732

Change #1195751 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Add hooks to handle user updates in event contributions

https://gerrit.wikimedia.org/r/1195751

Change #1196482 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] Use stored usernames in EventContributionsPager

https://gerrit.wikimedia.org/r/1196482

vaughnwalters subscribed.
Acceptance Criteria:
  • Given a user is on Special:EventDetails, contributions tab
    • And they sort the table by user
      • ✅ Then users should be sorted alphabetically according to their username
  • And if a user is renamed via either UserMerge, Renameuser, or CentralAuth's global rename. [NOTE: Some of these are for single-wiki envs and don't work when the others are installed. Check the docs]
    • ✅ Then their username in the table view is also updated
    • ✅ And sorting by user uses the new username
      Screenshot 2025-10-28 at 4.21.21 PM.png (988×1 px, 192 KB)
  • And if a user is hidden or deleted via CentralAuth, OR if it's suppressed via Special:Block in a single-wiki environment
    • ✅ Then their username is removed from the ce_event_contributions DB table
    • ❓ And the table view shows "Deleted user" or similar messages, unchanged from before this task QA note - something strange is happening here, it is not always display these users who have their status changed, even on hard refresh until i click to sort by ascending or descending. This is also intermittent and I am unable to reliably reproduce. I am inclined to just keep an eye on this until I can test in a live environment but wanted to ask if you have seen this also @Daimona before I mark it as done? see gif
      Screen Recording 2025-10-28 at 5.27.47 PM.gif (962×793 px, 3 MB)
    • ✅ And sorting by username treats all deleted/hidden users the same, regardless of their IDs
      Screenshot 2025-10-28 at 5.19.15 PM.png (1×1 px, 309 KB)
    • ✅ And if the user is later unhidden/undeleted, these changes are reverted **QA note - tested only for if the user is unhidden, as I am unable to undelete a user through Special:CentralAuth, as far as i know. And working as expected for unhiding a user"

**QA note - something strange is happening here, it is not always display these users who have their status changed, even on hard refresh until i click to sort by ascending or descending. This is also intermittent and I am unable to reliably reproduce. I am inclined to just keep an eye on this until I can test in a live environment but wanted to ask if you have seen this also @Daimona before I mark it as done?

This is definitely worrying and not meant to be happening. I tried to test this locally but got inconsistent results and there seems to be something wrong in the data. I still haven't determined if it's due to my local environment not being fully migrated or something else. Can you share the output of the following query, adding the actual event ID?

select cec_id,cec_user_id,cec_user_name,cec_page_prefixedtext,cec_timestamp from ce_event_contributions where cec_event_id=ID_you_are_using_to_test_this order by cec_timestamp desc;

**QA note - something strange is happening here, it is not always display these users who have their status changed, even on hard refresh until i click to sort by ascending or descending. This is also intermittent and I am unable to reliably reproduce. I am inclined to just keep an eye on this until I can test in a live environment but wanted to ask if you have seen this also @Daimona before I mark it as done?

This is definitely worrying and not meant to be happening. I tried to test this locally but got inconsistent results and there seems to be something wrong in the data. I still haven't determined if it's due to my local environment not being fully migrated or something else. Can you share the output of the following query, adding the actual event ID?

select cec_id,cec_user_id,cec_user_name,cec_page_prefixedtext,cec_timestamp from ce_event_contributions where cec_event_id=ID_you_are_using_to_test_this order by cec_timestamp desc;
> select cec_id,cec_user_id,cec_user_name,cec_page_prefixedtext,cec_timestamp from ce_event_contributions where cec_event_id=4903 order by cec_timestamp desc;

stdClass Object
(
    [cec_id] => 156
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024225020
)
stdClass Object
(
    [cec_id] => 155
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224929
)
stdClass Object
(
    [cec_id] => 154
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224832
)
stdClass Object
(
    [cec_id] => 153
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224756
)
stdClass Object
(
    [cec_id] => 152
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224724
)
stdClass Object
(
    [cec_id] => 151
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224652
)
stdClass Object
(
    [cec_id] => 150
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224634
)
stdClass Object
(
    [cec_id] => 149
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224621
)
stdClass Object
(
    [cec_id] => 148
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224612
)
stdClass Object
(
    [cec_id] => 147
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Hey
    [cec_timestamp] => 20251024224545
)
stdClass Object
(
    [cec_id] => 146
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Testderp
    [cec_timestamp] => 20251024224443
)
stdClass Object
(
    [cec_id] => 145
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.7145542849601654-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224433
)
stdClass Object
(
    [cec_id] => 144
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => 071724
    [cec_timestamp] => 20251024224412
)
stdClass Object
(
    [cec_id] => 143
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Hey
    [cec_timestamp] => 20251024224404
)
stdClass Object
(
    [cec_id] => 142
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Test2
    [cec_timestamp] => 20251024224338
)
stdClass Object
(
    [cec_id] => 141
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Test2
    [cec_timestamp] => 20251024224332
)
stdClass Object
(
    [cec_id] => 140
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224314
)
stdClass Object
(
    [cec_id] => 139
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224307
)
stdClass Object
(
    [cec_id] => 138
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224300
)
stdClass Object
(
    [cec_id] => 137
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224253
)
stdClass Object
(
    [cec_id] => 136
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224246
)
stdClass Object
(
    [cec_id] => 135
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224239
)
stdClass Object
(
    [cec_id] => 134
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224232
)
stdClass Object
(
    [cec_id] => 133
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224225
)
stdClass Object
(
    [cec_id] => 132
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224219
)
stdClass Object
(
    [cec_id] => 131
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224210
)
stdClass Object
(
    [cec_id] => 130
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224156
)
stdClass Object
(
    [cec_id] => 129
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224148
)
stdClass Object
(
    [cec_id] => 128
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => T388742
    [cec_timestamp] => 20251024224129
)
stdClass Object
(
    [cec_id] => 127
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => 0.24134670179703854-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224118
)
stdClass Object
(
    [cec_id] => 126
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => 0.24134670179703854-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224111
)
stdClass Object
(
    [cec_id] => 125
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Test2
    [cec_timestamp] => 20251024224059
)
stdClass Object
(
    [cec_id] => 124
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.663432303977435-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024224050
)
stdClass Object
(
    [cec_id] => 122
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.10845210552957374-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024011421
)
stdClass Object
(
    [cec_id] => 121
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024011354
)
stdClass Object
(
    [cec_id] => 120
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.6744879304494749-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251024010851
)
stdClass Object
(
    [cec_id] => 119
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Hey
    [cec_timestamp] => 20251023212625
)
stdClass Object
(
    [cec_id] => 118
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.8169227333307449-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212608
)
stdClass Object
(
    [cec_id] => 117
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Created-private
    [cec_timestamp] => 20251023212557
)
stdClass Object
(
    [cec_id] => 116
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.7145542849601654-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212523
)
stdClass Object
(
    [cec_id] => 115
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.2601442045300366-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212505
)
stdClass Object
(
    [cec_id] => 114
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => 0.6745615029917702-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212455
)
stdClass Object
(
    [cec_id] => 113
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Boobooboo
    [cec_timestamp] => 20251023212443
)
stdClass Object
(
    [cec_id] => 112
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Sup
    [cec_timestamp] => 20251023212429
)
stdClass Object
(
    [cec_id] => 111
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9149143437333767-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212417
)
stdClass Object
(
    [cec_id] => 110
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.04831037774536684-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212405
)
stdClass Object
(
    [cec_id] => 109
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.4253535740290262-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251023212354
)
stdClass Object
(
    [cec_id] => 105
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => 001
    [cec_timestamp] => 20251023210936
)
stdClass Object
(
    [cec_id] => 104
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => Testderp
    [cec_timestamp] => 20251023181809
)
stdClass Object
(
    [cec_id] => 103
    [cec_user_id] => 5422
    [cec_user_name] =>
    [cec_page_prefixedtext] => Created-private
    [cec_timestamp] => 20251022191752
)
stdClass Object
(
    [cec_id] => 102
    [cec_user_id] => 5422
    [cec_user_name] =>
    [cec_page_prefixedtext] => Hey
    [cec_timestamp] => 20251022191711
)
stdClass Object
(
    [cec_id] => 101
    [cec_user_id] => 5422
    [cec_user_name] =>
    [cec_page_prefixedtext] => BeforeEach-name-0.796156551693415-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251022190930
)
stdClass Object
(
    [cec_id] => 100
    [cec_user_id] => 5418
    [cec_user_name] => Test
    [cec_page_prefixedtext] => Ru2
    [cec_timestamp] => 20251022190813
)
stdClass Object
(
    [cec_id] => 99
    [cec_user_id] => 322
    [cec_user_name] =>
    [cec_page_prefixedtext] => Sup
    [cec_timestamp] => 20251022184901
)
stdClass Object
(
    [cec_id] => 98
    [cec_user_id] => 322
    [cec_user_name] =>
    [cec_page_prefixedtext] => Awef
    [cec_timestamp] => 20251022184846
)
stdClass Object
(
    [cec_id] => 97
    [cec_user_id] => 322
    [cec_user_name] =>
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.9412441725160914-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251022184835
)
stdClass Object
(
    [cec_id] => 96
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => BeforeEach-name-0.02514949656919896-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251022184730
)
stdClass Object
(
    [cec_id] => 95
    [cec_user_id] => 1
    [cec_user_name] => Admin
    [cec_page_prefixedtext] => TempUserSignup-TestPage-0.8169227333307449-Iñtërnâtiônàlizætiøn
    [cec_timestamp] => 20251022184636
)

Okay yeah, the pagination logic doesn't work well with a nullable column. I'll see what the easiest fix would be, possibly without needing another schema change.

(Also, the issue I had locally was due to the migration script I used, now fixed).

Change #1200073 had a related patch set uploaded (by Daimona Eaytoy; author: Daimona Eaytoy):

[mediawiki/extensions/CampaignEvents@master] EventContributionsPager: coalesce null names to string to fix sorting

https://gerrit.wikimedia.org/r/1200073

Change #1200073 merged by jenkins-bot:

[mediawiki/extensions/CampaignEvents@master] EventContributionsPager: coalesce null names to string to fix sorting

https://gerrit.wikimedia.org/r/1200073

@vaughnwalters This should be fixed now, I would suggest testing somewhat thoroughly to make sure this works with paging and sorting... Nulls are tricky!

@vaughnwalters This should be fixed now, I would suggest testing somewhat thoroughly to make sure this works with paging and sorting... Nulls are tricky!

This seems to be working correctly as I can no longer reproduce that failure. I did create T408855 while testing this, but it doesn't seem to be related. So, sending to product sign off.

Just out of curiosity here - was the root cause that the following user had null for username? Which is acceptable data because it's a nullable column.

stdClass Object
(
    [cec_id] => 99
    [cec_user_id] => 322
    [cec_user_name] =>
    [cec_page_prefixedtext] => Sup
    [cec_timestamp] => 20251022184901
)

Just out of curiosity here - was the root cause that the following user had null for username? Which is acceptable data because it's a nullable column.

Yes, that combined with the fact that the pagination logic in MediaWiki core uses comparisons such as cec_user_name > 'Foo' OR (cec_user_name = 'Foo' AND cec_timestamp > '123') when iterating through subsequent result pages. I didn't consider this aspect when designing the schema and choosing null (as opposed to empty strings) for the column.

Makes sense, thanks. Like you mentioned, null is tricky. Glad you figured it out!