What had been happening was related to the fact that ATOMIC_REQUESTS is now on,
preventing writing to the database until a request is completed.
The problem with that was that CohortMemberships, with their explicit
transaction handling, were breaking that rule and touching the database.
The CohortMembership needs to also touch the database to update the
underlying course_user_groups information, to prevent the database from
getting into a strange state.
After this commit, every time a CohortMembership is updated or created, the
corresponding course_user_groups are also updated and explicitly written to
the database. This occurs atomically with the CohortMembership, and does not
wait for the end of the request.
On devops recommendation, now handling the potential for an 'inconsistency
window' via a management command instead of a hacky "re-run the data migration"
bash script.
By using the before_after library, we can force a race condition to reliably
occur in the CohortMembership.save() method. This unit test will do just that,
and ensure that our race-condition-handling code functions as it should.
The code changes needed to get CohortMembership functioning properly.
The key of this change is twofold: first, CohortMemberships are unique
per-user, per-course. This is enforced at the database level. Secondly,
the updates are done using a select_for_update, which ensures atomicity.
These are the migrations needed for CohortMembership to function.
0005 establishes the table, 0006 will be used to move existing data
into the table as needed.
Per product guidance, we can just arbitrarily reassign problem users.
Implementing that decision, as well as the remainder of the data migration.
Also including a short script to re-run the 0006 migration after code changes
are live, to prevent a potential issue where the database become out-of-sync.
* Automatically create user partitions on course publish for each ICRV checkpoint.
* Disable partitions for ICRV checkpoints that have been deleted.
* Skip partitions that have been disabled when checking access.
* Add verification access control UI to visibility settings.
* Add verification access control UI to sequential and vertical settings.
* Add partition scheme for verification partition groups.
* Cache information used by verification partition scheme and invalidate the cache on update.
* Add location parameter to UserPartition so the partition scheme can find the associated checkpoint.
* Refactor GroupConfiguration to allow multiple user partitions.
* Add special messaging to ICRV for students in the honor track.
Authors: Zubair Arbi, Awais Qureshi, Aamir Khan, Will Daly
The existing pattern of using `override_settings(MODULESTORE=...)` prevented
us from having more than one layer of subclassing in modulestore tests.
In a structure like:
@override_settings(MODULESTORE=store_a)
class BaseTestCase(ModuleStoreTestCase):
def setUp(self):
# use store
@override_settings(MODULESTORE=store_b)
class ChildTestCase(BaseTestCase):
def setUp(self):
# use store
In this case, the store actions performed in `BaseTestCase` on behalf of
`ChildTestCase` would still use `store_a`, even though the `ChildTestCase`
had specified to use `store_b`. This is because the `override_settings`
decorator would be the innermost wrapper around the `BaseTestCase.setUp` method,
no matter what `ChildTestCase` does.
To remedy this, we move the call to `override_settings` into the
`ModuleStoreTestCase.setUp` method, and use a cleanup to remove the override.
Subclasses can just defined the `MODULESTORE` class attribute to specify which
modulestore to use _for the entire `setUp` chain_.
[PLAT-419]