Page MenuHomePhabricator

[keyserver] Change updateRoles to support Voiced role
AbandonedPublic

Authored by rohan on Nov 10 2023, 2:10 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sat, Jun 29, 8:40 AM
Unknown Object (File)
Wed, Jun 26, 10:29 AM
Unknown Object (File)
Tue, Jun 25, 2:02 AM
Unknown Object (File)
Sat, Jun 22, 10:48 PM
Unknown Object (File)
Fri, Jun 21, 3:42 AM
Unknown Object (File)
Sat, Jun 15, 1:05 AM
Unknown Object (File)
Fri, Jun 14, 9:18 AM
Unknown Object (File)
Wed, Jun 12, 4:52 AM
Subscribers

Details

Reviewers
atul
ginsu
ashoat
Summary

updateRoles needs to be changed to support the Voiced role when a thread's type is changed.

For converting a channel from a regular channel to an announcement channel, we need to first create a Vocied role. Then, we need to query the roles in the community channel that have voiced_in_announcement_channels, and if the member has that role give them Voiced, otherwise give them Members.

For converting a channel from an announcement channel to a regular channel, I can take advantage of the deleteRole function I created that will both delete the role and assign the affected users to the default role of the thread (without triggering any messages in chat).

Addresses ENG-5702

Test Plan

Converting a regular channel to an announcement channel:

  • Created a regular subchannel with four members
  • Created a community role that had VOICED_IN_ANNOUNCEMENT_CHANNELS
  • Assigned it to two members
  • Ran yarn script dist/scripts/make-channel-private.js to convert the channel to type: threadTypes.COMMUNITY_ANNOUNCEMENT_OPEN_SUBTHREAD
  • Confirmed that the if (rolePermissions.Voiced && !currentRolePermissions.Voiced) { condition was called
  • Confirmed that a Voiced role was created
  • Confirmed that the users who had the community role with VOICED_IN_ANNOUNCEMENT_CHANNELS had their channel role set to the Voiced role. The other users remained with the Members role

Converting an announcement channel to a regular channel:

  • Patched the changes from D9805 that makes new members added to an announcement channel have the 'Voiced' role
  • Created an announcement channel and added four members
  • Confirmed that each of these members had the Voiced role in the memberships table
  • Ran yarn script dist/scripts/make-channel-private.js to convert the channel to type: threadTypes.COMMUNITY_OPEN_SUBTHREAD
  • Confirmed that the else if (!rolePermissions.Voiced && currentRolePermissions.Voiced) { condition was called
  • Confirmed that the Voiced role was deleted
  • Confirmed that the memberships table was updated so the four members who previously had Voiced were assigned the default role for the channel (Members)

Diff Detail

Repository
rCOMM Comm
Branch
combined_branch
Lint
No Lint Coverage
Unit
No Test Coverage

Event Timeline

Use promises array for role creation too

rohan requested review of this revision.Nov 10 2023, 2:53 PM
ashoat requested changes to this revision.Nov 11 2023, 4:06 PM
ashoat added inline comments.
keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)

This seems like a really complicated query.

Can you try to explain what it's doing, and why it needs to be so complicated?

Can you also share what you've done to analyze the query's performance? A good example of query performance analysis is the test plan in D9598. It might also be good to share some test queries with me that I can run on my keyserver in production. You can devise a SELECT version of this UPDATE

99–107 ↗(On Diff #33088)

Can you indent all of these lines one more (indented for the "WHERE" clause)

103–105 ↗(On Diff #33088)

Won't the set of this innermost query be way too large? It seems like it would be better to search for threads with root types, or at least to add a GROUP BY in here (I don't know if it's automatically implied here by the query engine or something)

104–106 ↗(On Diff #33088)

Can you indent these three lines one more?

106 ↗(On Diff #33088)

Try to keep lines below 80 chars

111 ↗(On Diff #33088)

What are you going to do with the users that currently have Voiced?

This revision now requires changes to proceed.Nov 11 2023, 4:06 PM
rohan added a subscriber: michal.
rohan added inline comments.
keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)

Explanation
The goal of the query is to update the roles for users who were in a regular subchannel but are now in an announcement subchannel.

By default, they are all given the default role (Members). Now that the channel has been converted to an announcement channel, we need to selectively give Voiced to some users if they have the thread permission voiced_in_announcement_channels in the community root.

So the subquery

SELECT m.user
          FROM memberships AS m
          JOIN roles AS r ON m.role = r.id
          WHERE r.thread IN (
              SELECT community FROM threads WHERE id = ${threadID}
            )
            AND JSON_EXTRACT(r.permissions, 
              '$.voiced_in_announcement_channels') = true

looks for users in the community root who have the voiced_in_announcement_channels thread permission. The JOIN on roles is so we can perform a JSON_EXTRACT on the role permissions column. The WHERE statement helps limit the threads we are searching to just one. This subquery ends up in a set of user ids.

The outer query then updates the role for these users in memberships to the newly created Voiced role.

Performance
To analyze the performance, I followed @michal's test plan in the diff you linked. I wrote an EXPLAIN query. Two of the queries are of type ref, one is eq_ref, and one is const. Both const and eq_ref are regarded as being the best types of joins.

This query also only runs when a regular subchannel is converted to an announcement subchannel.

Here's a sample SELECT query you could run on production (though I'm not expecting any results since voiced_in_announcement_channels is not a thread permission on master as of yet):

SELECT *
FROM memberships
WHERE thread = ${threadID}
  AND user IN (
    SELECT m.user
    FROM memberships AS m
    JOIN roles AS r ON m.role = r.id
    WHERE r.thread IN (
        SELECT community FROM threads WHERE id = ${threadID}
      )
      AND JSON_EXTRACT(r.permissions, '$.voiced_in_announcement_channels') = true
  );
103–105 ↗(On Diff #33088)

Maybe I'm misunderstanding your comment, but the expected result set of SELECT community FROM threads WHERE id = ${threadID} is just one

111 ↗(On Diff #33088)

This is already handled by deleteRole, but they get assigned to the default role (Members)

keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)

Thanks for the explanation!

  1. That test query has a syntax error on ${threadID}. Can you try again, and this time make sure to test the query before sending?
  2. In additional to the other syntax feedback I previously shared, please make sure to remove AS where it's not necessary (all table aliases), and to remove the alias entirely where it's not used (such as main)? In general, please try harder to match our conventions around SQL – with every decision, look to see how it's done in the codebase. It will save both of us a ton of time and annoyance and typing.
  3. Can you extract this query to a function that is named in a way that makes it clear what the query is doing?
103–105 ↗(On Diff #33088)

Oops sorry, you're right

111 ↗(On Diff #33088)

Thanks for explaining

rohan added inline comments.
keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)
  1. That's weird. I tested before sending and I've just copied and pasted the query into TablePlus and it runs fine? Do I need to adjust it for the command line where you might be running it?

For 2 and 3, sure I'll go ahead and make that change

keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)

Perhaps I'm being unclear...

The string literal ${threadID} is not valid SQL syntax. Please provide a query I can copy-paste. I don't know which threadID to use

keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)
SELECT *
FROM memberships
WHERE thread = 311765
  AND user IN (
    SELECT m.user
    FROM memberships AS m
    JOIN roles AS r ON m.role = r.id
    WHERE r.thread IN (
        SELECT community FROM threads WHERE id = 311765
      )
      AND JSON_EXTRACT(r.permissions, '$.voiced_in_announcement_channels') = true
  );
keyserver/src/updaters/role-updaters.js
96–107 ↗(On Diff #33088)

Completed in 0.020s! Looks good

Address feedback (extract query to a function and fix formatting).

Also repeated the 'Converting a regular channel to an announcement channel' test plan

ashoat requested changes to this revision.Nov 19 2023, 10:41 AM

This is close, but one last round here

keyserver/src/updaters/role-updaters.js
15

You should pretty much never use the async keyword if you're not using the await keyword inside the function

35

cc @atul, we're adding one more of these in-SQL permission checks

38

You're creating this Promise but not returning or awaiting it. You should probably return the dbQuery Promise directly (in which case strip async keyword above). Alternately you could keep the async keyword and replace this line with return await dbQuery(updateMembershipsQuery);, but that adds another layer of Promise creation that's probably not necessary

107–121

This should probably be applied to the existing code too

This revision now requires changes to proceed.Nov 19 2023, 10:41 AM
keyserver/src/updaters/role-updaters.js
38

Actually the "alternately" proposal doesn't need return await, just await

rohan edited the summary of this revision. (Show Details)

Abandoning this for now since we're reconsidering the Voiced approach