-
Notifications
You must be signed in to change notification settings - Fork 1
docs: add ADR for updating user groups based on criteria definitions #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: MJG/runtime-architecture-for-evaluation-and-extension-adr
Are you sure you want to change the base?
Conversation
|
Thanks for the pull request, @mariajgrimaldi! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. 🔘 Update the status of your PRYour PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate. Where can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
4d2bd76 to
aeda019
Compare
66e51dc to
d0030ec
Compare
8ca6e86 to
080c904
Compare
d0030ec to
ef50eb8
Compare
f8775da to
4abee11
Compare
ef50eb8 to
5d92f6a
Compare
| Define Exclusivity Domains Through Update Framework | ||
| --------------------------------------------------- | ||
|
|
||
| * **Automatic Exclusivity Domains**: When the criteria of group G1 and group G2 are mutually exclusive (C1, ..., Cn ∩ C'1, ..., C'n = ∅), these groups automatically form a **mutual exclusivity domain** that is managed by the event-based update framework. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need for a mechanism to enforce this? If all criteria from all relevant groups are evaluated within the same transaction this would seem to be handled automatically. I'm not sure if this section and the following one are just documenting this property of the system or are suggesting the system will try to add additional checks on top of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mutual exclusivity domains apply to criteria that are naturally mutually exclusive. For example, the IsStaff criterion, a user is either staff or not, but never both at the same time. The reason a user doesn't end up in both groups is because we have a mechanism that evaluates all criteria of the same type at once, in a single transaction.
I don't think we need an additional mechanism beyond that, but I might be missing some edge cases. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that holds up in the ORM case, but I'm not sure about what happens with race conditions once we add in external systems whose data may not update in real time. I guess they might, by necessity, force the system out of a mutual exclusivity domain unless we had another mechanism to enforce it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I'll add this as a note under this section. Thank you!
| * **Hybrid Approach**: The combination of Group Collections + refresh & consistency framework guarantees that a user is never in two groups that are mutually exclusive by nature (contradictory), whether the exclusivity is: | ||
|
|
||
| * **Natural/Automatic**: Derived from mutually exclusive criteria (handled by update framework) | ||
| * **Administrative/Manual**: Defined by course staff or admin users (handled by Group Collections) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is the only case where we might need collections, and I'm not sure it's a requirement. If I'm understanding it correctly, this would cover cases like:
-
Course admin uploads a list of users who are learning hybrid online/in-person and wants that group to be exclusive of student who have taken an online-only entrance exam
-
Site admin uploads a list of teaching assistants for one group, but doesn't want it to include people who are course admins on any course
I think this is problematic in a couple of ways:
- In scenario 1 if a student accidentally takes the exam before being put into the manually uploaded sheet they are forever forbidden from joining the correct group
- In scenario 2 if a TA becomes a course admin after the sheet is uploaded they will be forbidden from joining the "course admin" group with no way to resolve the issue aside from removing the user from the manual list, having them be re-added as a course admin. If user group errors like this bubble up to the upstream event it could cause the user to not be able to be promoted to a course admin at all, I think?
@crathbun428 can you think of a requirement where we would need this case that's less contrived than what I said above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think that Event-Based Exclusivity Management should be enough to handle the mutual exclusivity examples we had originally talked about - https://openedx.atlassian.net/wiki/spaces/OEPM/pages/5004591109/Mutual+Exclusivity+in+User+Groups#Examples-of-Mutually-Exclusive-Groups
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only instance i could think of is if you wanted a group that excluded another group but didn't specify the requirements. like, group1 = 'on-campus users who are not in the at-risk user group (group2)' so instead of specifying all of the at-risk conditions in group1, you would just say group1 and group2 are mutually exclusive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be useful to list the use cases we want to address with explicitly defined mutually exclusive groups. I initially thought of these examples, but I'm definitely missing others like the ones you shared.
What's straightforward about my example is that the user can only be in one group or the other, so it's obvious where they belong. But in cases like: group1 = on-campus users who are not in the at-risk user group (group2), if a user ends up in both groups and they're mutually exclusive, would that mean the user is removed from both? In this kind of scenario, it might be simpler to just use criteria intersections rather than group collections.
I had initially only considered these types of exclusivity:
-
Negation of a criterion (C ∩ ¬C = ∅)
- The group collection comes from the criterion itself.
- Example: "Final Exam Result" as the collection, with groups like
Passed Final ExamandDid Not Pass Final Exam.
-
Unique criteria
- The group collection is based on a property that is inherently unique per user.
- Example: "Country of Residence" (
Spain,France,Germany) or "Language Proficiency" (Beginner,Intermediate,Advanced).
-
Arbitrary or random assignment
- The group collection is created for controlled assignment.
- Example: "A/B Test Groups" (
Experiment A,Experiment B), where each user is randomly assigned to one group.
Can you think of others?
For your examples (hybrid learners vs. exam takers, or TAs vs. course admins), they fit best into the negation/intersection case. Event-based exclusivity should be enough to handle those without needing explicit collections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mariajgrimaldi - why do we need a collection for an automatic exclusivity? for your example using enrollment mode, if a user changes from 'audit' to 'honor' then the code should automatically move them from one group to another according to the group definitions, i don't see why we would need a collection for these groups?
i think Ty was specifically talking about manually defined exclusivity groups and if this would work/is necessary. it seems like the only case that a manually defined collection would be needed is the A/B test group but maybe that could be done with a csv upload?
|
|
||
| * **External data**: Account type groups ("Free Tier", "Premium", "Enterprise") updated from external billing system daily - all updated together in the same batch operation | ||
| * **Missing events**: User skill level groups ("Beginner", "Intermediate", "Advanced") where skill assessment data exists but events aren't implemented yet - updated together via scheduled refresh | ||
| * **Performance constraints**: Heavy analytics-based groups that are too expensive to update in real-time - updated together during off-peak hours |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think based on what was said above, these would be triggered by any of the other refresh cadences as well since triggering any criteria in a group triggers an evaluation of all criteria for that group?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True! I'll drop this line
|
|
||
| To give operators flexibility in managing the refresh framework, we will: | ||
|
|
||
| * **Group Freezing**: Allow freezing updates for a group (stop all refreshes temporarily), useful for operational debugging or data stability. Frozen groups will not be visible to the orchestrator until unfrozen. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where would these overrides be stored? I don't think there is currently a place for them on the group model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the same as making a group 'inactive'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be easier (?) to allow you to make a point-in-time copy of a group (so say you have dynamic-group-a that refreshes daily, and I could make a copy of that group, dynamic-group-a-{timestamp} that is the copy of that group at a specific moment, but that would allow the dynamic group to still exist)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the same as making a group 'inactive'?
Yes! I was referring to disabling a group, although this would have different consequences, not only stopping updates.
Would it be easier (?) to allow you to make a point-in-time copy of a group (so say you have dynamic-group-a that refreshes daily, and I could make a copy of that group, dynamic-group-a-{timestamp} that is the copy of that group at a specific moment, but that would allow the dynamic group to still exist)
In this case, where a group is misbehaving in some way, if we allow the group to exist, it could negatively impact the student experience if the group was used for content gating, for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! I was referring to disabling a group, although this would have different consequences, not only stopping updates.
What other consequences are there?
|
|
||
| * **Group Freezing**: Allow freezing updates for a group (stop all refreshes temporarily), useful for operational debugging or data stability. Frozen groups will not be visible to the orchestrator until unfrozen. | ||
|
|
||
| * **Frequency Overrides**: Allow operational overrides of refresh frequencies for individual groups or criteria when needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are allowing overrides at the criteria level we would need a place to store this as well, but I'm not sure if that complicates things too much. Since we've said that if any other criteria is being triggered they should also be triggered as well I'm not sure if this is possible, or if we're saying we would just ignore that requirement in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to drop this since it's too much of a requirement and we don't have a specific use case for this at the moment.
|
|
||
| * **Frequency Overrides**: Allow operational overrides of refresh frequencies for individual groups or criteria when needed. | ||
|
|
||
| * **Method Restrictions**: Support restricting groups to a single update method (event-only, scheduled-only, or manual-only) when operationally required. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this saying you could force an event-driven criterion to be refreshed on a schedule? wouldnt that involve changing the whole backend of the criterion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each criterion should support all three update strategies if possible (manual updates don’t make much sense in most cases). However, we can limit a group to update only on specific events or on a scheduled basis. For example, if a group is very large and gets updated too often, we can switch it to scheduled-only updates to avoid putting too much load on the system.
Now this would mean we should enable update strategies overrides at some point, which makes things more complicated.
I think for now we should go with only the update strategy configured in the criteria, nothing else. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes if we only use the update strategy configured in the criteria then that makes sense
| * Trigger re-evaluation when any criterion's update frequency threshold is reached (scheduled update). Example: If C1 is event-based and C2 is cached daily, the group is refreshed: | ||
|
|
||
| * Immediately on C1 events. | ||
| * On scheduled daily refresh for C2 (unless already refreshed by C1 events). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(unless already refreshed by C1 events).
Would this be more difficult to implement than just doing a scheduled refresh regardless of C1 events? If I know that C2 events are processed at 1800 each night so I schedule a mailing after 1800 expecting the freshest data, but a C1 event happened at 1802 the day before, would there actually not be a refresh of the criterion as I had expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmmm totally. You're right about this creating an inconsistency regarding fo what the user actually expects. I'll remove the annotation and replace it with regardless of C1 events 1ea8c26
| * Immediately on C1 events. | ||
| * On scheduled daily refresh for C2 (unless already refreshed by C1 events). | ||
|
|
||
| * Set refresh frequency per criterion type based on data volatility and system performance requirements, as outlined in the long-term requirements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be tunable by site operators? (not sure if necessary just curious)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| To give operators flexibility in managing the refresh framework, we will: | ||
|
|
||
| * **Group Freezing**: Allow freezing updates for a group (stop all refreshes temporarily), useful for operational debugging or data stability. Frozen groups will not be visible to the orchestrator until unfrozen. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be easier (?) to allow you to make a point-in-time copy of a group (so say you have dynamic-group-a that refreshes daily, and I could make a copy of that group, dynamic-group-a-{timestamp} that is the copy of that group at a specific moment, but that would allow the dynamic group to still exist)
4abee11 to
c29e7a8
Compare
5d92f6a to
e6b794a
Compare
0b1ca0b to
3e4aa5a
Compare
|
Friendly ping on this @saraburns1! |
|
@mariajgrimaldi can you put the FC number in the title of FC prs (this and the other User Groups PRs) to help Michelle triage appropriately? Also I think we're holding on user groups so maybe these should be converted to draft? |
Description
Add a follow-up ADR with the decisions regarding how user groups are updated throughout their own lifecycle.
This ADR is result from team conversations during a Spike phase, which produced several architectural documents. You can find them here: https://openedx.atlassian.net/wiki/spaces/OEPM/pages/4901404678/User+Groups. This specific ADR is a result of the discussions had in these two documents:
For detailed implementation information, see the entire technical approach here: https://openedx.atlassian.net/wiki/spaces/OEPM/pages/4917657604/Unified+User+Group+Model+Technical+Approach
Here is also a proof of concept (POC) designed to test these decisions, along with the other ADRs: #7
Merge checklist:
Check off if complete or not applicable: