Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.

Conversation

@mxstbr
Copy link
Contributor

@mxstbr mxstbr commented Nov 26, 2018

This adds two new indexes to usersChannels and rewrites all queries to use them:

  • userIdAndRole
  • channelIdAndRole

"Role" is one of the roles a user can have in a channel, and is based on the is{Role} fields, i.e. "Role" can be one of "blocked", "pending", "owner", "moderator", "owner".

Note that the index prefers roles in that order, so even if a write query is inconsistent and a member has isMember: true but also isBlocked: true the user will be counted as blocked.


This is modeled after the same thing we did for usersCommunities, but I realized that adding 5 indexes to a table as big as usersCommunities and usersChannels is pretty expensive storage-wise. (I could've also implemented the same indexes as usersCommunities, i.e. userIdAndIsMember, userIdAndIsBlocked, etc.)

This achieves the same thing and makes all queries be indexed, but requires a lot less storage. We should probably also move the usersCommunities indexes to this *AndRole system...

Status

  • WIP
  • Ready for review
  • Needs testing

Deploy after merge (delete what needn't be deployed)

  • api
  • analytics
  • athena
  • vulcan
  • mercury
  • hermes
  • chronos

Run database migrations (delete if no migration was added)
YES

Related issues
Based on #4339

This was leftover behavior from the move to create-query, but is
actually unnecessary.
This adds two new indexes to `usersChannels` and rewrites all queries to
use them:

- `userIdAndRole`
- `channelIdAndRole`

"Role" is one of the roles a user can have in a channel, and is based on
the `is{Role}` fields, i.e. "Role" can be one of "blocked", "pending",
"owner", "moderator", "owner".

Note that the index prefers roles in that order, so even if a write
query is inconsistent and a member has `isMember: true` but also
`isBlocked: true` the user will be counted as blocked.

----

This is modeled after the same thing we did for `usersCommunities`, but
I realized that adding 5 indexes to a table as big as `usersCommunities`
and `usersChannels` is pretty expensive storage-wise. (I could've also
implemented the same indexes as `usersCommunities`, i.e.
`userIdAndIsMember`, `userIdAndIsBlocked`, etc.)

This achieves the same thing and makes all queries be indexed, but
requires a lot less storage. We should probably also move the
usersCommunities indexes to this `*AndRole` system...
.getAll(currentUser, { index: 'userId' })
.filter({ isBlocked: false, isMember: true })
.getAll(
[evalUser, 'member'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be currentUser not evalUser?

.getAll(currentUser, { index: 'userId' })
.filter({ isBlocked: false, isMember: true })
.getAll(
[evalUser, 'member'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentUser not evalUser right?

.table('usersChannels')
.getAll(userId, { index: 'userId' })
.filter({ channelId, isBlocked: false })
.getAll([userId, channelId], { index: 'userIdAndChannelId' })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still want the isBlocked filter here right? Otherwise someone could spoof joining a channel via a direct POST request

.getAll(channelId, { index: 'channelId' })
.filter({ isMember: true })
.getAll(
[channelId, 'member'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: this automatically handles duplication? e.g. someone is a moderator and a member, it should only count as one membership

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, since the role index can only have one value the users that have a role of "member" are exclusive of other users who have the roles "moderator" or "owner". See my comment below: #4340 (comment)

.table('usersChannels')
.getAll(userId, { index: 'userId' })
.filter({ isMember: true })
.getAll([userId, 'member'], [userId, 'moderator'], [userId, 'owner'], {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed you've done this check in a few places. If we're just trying to get members, why are you using all 3 indexes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because owners and moderators are also members, but their roles are "owner" and "moderator"!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't true, so we can simplify this! If you are a moderator or owner you also are isMember: true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't, unfortunately!

Indexes can only have a single value, not multiple values. The "role" index is set to the highest one based on this precedence:

  • Blocked
  • Pending
  • Owner
  • Moderator
  • Member

This means:

  • If both isMember and isOwner are true, the users role will be "owner".
  • If both isMember and isModerator are true, the users role will be "moderator".
  • If both isMember and isBlocked are true (for whatever reason, bad db data most likely) the the role will be "blocked".

That means if we want to get all members we also have to fetch the users who have moderator and owner roles, on top of the ones that have member roles.

Does that make sense?

@mxstbr
Copy link
Contributor Author

mxstbr commented Nov 26, 2018

Great reviews, will dig further into this tomorrow unless you want to dig into this and finish it off today!

@brianlovin
Copy link
Contributor

Great reviews, will dig further into this tomorrow unless you want to dig into this and finish it off today!

Will be testing some other work today and trying to fix some other issues with Chronos, so feel free to finish up as you have time this week ;)

@mxstbr
Copy link
Contributor Author

mxstbr commented Nov 27, 2018

Cleaned up those copy-and-paste goofs in the queries with evalUser and currentUser, want to give this another good review and ship if you're happy with it?

@brianlovin
Copy link
Contributor

Sure thing @mxstbr. One question about this new index: how is the performance? Do all the branches end up taking longer than a normal query without an index?

@mxstbr
Copy link
Contributor Author

mxstbr commented Nov 27, 2018

Indexes are computed at write-time, rather than read-time, so those r.branches won't have any effect on the read-time of queries and a very neglible impact on write-time, although I admittedly haven't tested that (and don't even know how I would...).

.table('usersChannels')
.getAll(channelId, { index: 'channelId' })
.filter({ userId, isBlocked: true })
.getAll([userId, channelId], { index: 'userIdAndChannelId' })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, one more spot: I think we want to filter isBlocked: true to ensure that it's only approving a blocked user

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Committing

@brianlovin
Copy link
Contributor

Afaict in my local testing everything is working well! I double checked search filtering and profile filtering based on private communities and channels, and everything is holding up.

One thing I noticed: at some point along the way we made it impossible to fetch a private channel where you're not a member. This means we never render the 'request to join private channel' view :P Not high pri, but we probably want that request-to-join screen back at some point.

@brianlovin
Copy link
Contributor

Gonna merge and add this to 2.7.8

@brianlovin brianlovin merged commit d832592 into alpha Nov 27, 2018
@brianlovin brianlovin deleted the users-channels-role-index branch November 27, 2018 18:14
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants