Skip to content
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

Learner devices status update #12153

Merged

Conversation

rtibbles
Copy link
Member

@rtibbles rtibbles commented May 9, 2024

Summary

  • Turns the learner device status user field to a ForeignKey rather than OneToOne field to prevent errors when trying to create learner device statuses for the same learner for syncs from multiple devices
  • Adds sync hook to selectively delete LearnerDeviceStatus to prevent trying to sync more than one LearnerDeviceStatus for a single user to another device.

References

Fixes #12043

Reviewer guidance

The logic is relatively straight forward, I think - I am just not sure how to do any sort of test of this change, especially the version sync hook.

QA specific guidance:

Install the PR asset onto a server device and onto two single user devices.

Setup syncing with those two single user devices to the server for the same learner account.

Setup 0.16.1 onto another server device, and import the facility.

Setup another single user syncing device using 0.16.1 and import the same learner again.

Ensure syncing continues unabated.


Testing checklist

  • Contributor has fully tested the PR manually
  • If there are any front-end changes, before/after screenshots are included
  • Critical user journeys are covered by Gherkin stories
  • Critical and brittle code paths are covered by unit tests

PR process

  • PR has the correct target branch and milestone
  • PR has 'needs review' or 'work-in-progress' label
  • If PR is ready for review, a reviewer has been added. (Don't use 'Assignees')
  • If this is an important user-facing change, PR or related issue has a 'changelog' label
  • If this includes an internal dependency change, a link to the diff is provided

Reviewer checklist

  • Automated test coverage is satisfactory
  • PR is fully functional
  • PR has been tested for accessibility regressions
  • External dependency files were updated if necessary (yarn and pip)
  • Documentation is updated
  • Contributor is in AUTHORS.md

🚀 This description was created by Ellipsis for commit 2d91ec9

Summary:

This PR updates the LearnerDeviceStatus model to support multiple device statuses per user and adds a sync operation to manage data consistency across device syncs.

Key points:

  • Changed LearnerDeviceStatus.user from OneToOneField to ForeignKey.
  • Added LearnerDeviceStatusOperation to handle deletion of problematic LearnerDeviceStatus records during sync.
  • Introduced LearnerDeviceStatusHook to trigger the new operation during sync.
  • Added migration 0022_learner_device_status_multiple_devices.py for database schema update.

Generated with ❤️ by ellipsis.dev

@rtibbles rtibbles added this to the Kolibri 0.16: Planned Patch 2 milestone May 9, 2024
@rtibbles rtibbles requested a review from bjester May 9, 2024 21:32
@rtibbles rtibbles added the TODO: needs review Waiting for review label May 9, 2024
@github-actions github-actions bot added DEV: backend Python, databases, networking, filesystem... SIZE: small labels May 9, 2024
@rtibbles
Copy link
Member Author

Hey @ellipsis, give me a code review

Copy link

ellipsis-dev bot commented May 10, 2024

OK! Reviewing this PR...


Responding to this comment by @rtibbles. For more information about Ellipsis, check the documentation.

Copy link

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

👍 Looks good to me! Reviewed everything up to 2d91ec9 in 1 minute and 31 seconds

More details
  • Looked at 103 lines of code in 3 files
  • Skipped 0 files when reviewing.
  • Skipped posting 3 drafted comments based on config settings.
1. /kolibri/core/device/migrations/0022_learner_device_status_multiple_devices.py:20
  • Draft comment:
    The migration correctly updates the 'user' field from OneToOneField to ForeignKey to support multiple device statuses per user, aligning with the PR's intent.
  • Reason this comment was not posted:
    Confidence changes required: 0%
    The migration file correctly changes the 'user' field in the 'LearnerDeviceStatus' model from a OneToOneField to a ForeignKey. This change is necessary to allow multiple device statuses per user, which is the intent of the PR as described. The migration dependencies are correctly set to the last migration in the device app. The field changes are appropriate and use CASCADE for deletion, which is standard for user-related foreign keys in Django.
2. /kolibri/core/device/models.py:613
  • Draft comment:
    The change from OneToOneField to ForeignKey for the 'user' field in 'LearnerDeviceStatus' is correctly implemented to support multiple statuses per user.
  • Reason this comment was not posted:
    Confidence changes required: 0%
    The model change in 'LearnerDeviceStatus' from OneToOneField to ForeignKey for the 'user' field is correctly implemented. This change is necessary to support multiple device statuses per user, which is the intent of the PR. The related_name 'learner_device_status' is appropriately set to handle reverse relations from the 'FacilityUser' model.
3. /kolibri/core/device/kolibri_plugin.py:75
  • Draft comment:
    The LearnerDeviceStatusOperation class handles the downgrade scenario effectively by deleting excess LearnerDeviceStatus records that could cause issues when syncing to a previous version.
  • Reason this comment was not posted:
    Confidence changes required: 0%
    The LearnerDeviceStatusOperation class is introduced to handle the downgrade scenario when syncing to a previous version that does not support multiple LearnerDeviceStatus records per user. The logic checks if it's a single user sync and deletes statuses accordingly, or deletes all statuses for a facility if it's a facility sync. This is a crucial addition to handle data consistency across different versions.

Workflow ID: wflow_Q6VkcI9MinG9SO0s


You can customize Ellipsis with 👍 / 👎 feedback, review rules, user-specific overrides, quiet mode, and more.

6 days left in your free trial, upgrade for $20/seat/month or contact us.

Copy link
Member

@bjester bjester left a comment

Choose a reason for hiding this comment

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

More specific handling is required when the device is the pusher.

context.sync_session.client_instance_id
if context.is_server
else context.sync_session.server_instance_id
)
Copy link
Member

Choose a reason for hiding this comment

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

So this downgrade method is called when this device is the producer of sync data. When it's a pull, then it must be the server if it's the producer, so restricting the learner status to only the client_instance_id device makes sense.

Although, when it's a push, in order for the device to only send one status, it can first consider whether the current device has created a sync status. If the current device has, we would still want to exclude the client_instance_id, because being a push producer (aka not context.is_server), the client's status is still what matters. If the current device hasn't created one, then we would want to delete all but the most recent user status.

So in summary, I don't think this code will function as expected when the device is pushing. For pulling, this looks okay.

When it's pushing without a user sub-partition, we would want to ensure the statuses are unique for each user, preferably using the most recent status for each user, meaning no explicit filtering on the instance. So the same approach for pulling doesn't quite work for pushing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks @bjester - I didn't think I'd get it right first time. If you have any ideas on how to write a robust test case for this, so I can be sure of covering the bases, I'm very open to inspiration!

Copy link
Member

Choose a reason for hiding this comment

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

Honestly coming back to the conversation about this, it was difficult to wrap my head around it! I realized the logic isn't straightforward at all 🫠

I was wondering about testing. Since this relies on different versions, it doesn't seem straightforward either. The KolibriVersionedSyncOperationTestCase demonstrates how you could possibly set up a unit test. For sync integration tests, I believe it could be possible to set one up overriding CUSTOM_INSTANCE_INFO which influences the version that morango uses and communicates during syncing.

"MORANGO_INSTANCE_INFO": "kolibri.core.auth.test.sync_utils:CUSTOM_INSTANCE_INFO"

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yeah, I had forgotten about the custom instance info, that does seem like the right way to go for the integration tests. Will take a look at the version sync tests too, thanks!

Copy link
Member

@bjester bjester left a comment

Choose a reason for hiding this comment

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

The latest updates make sense! This logic will make you loopy ➿

@pcenov
Copy link
Member

pcenov commented May 15, 2024

Hi @rtibbles no issues observed while checking the above described QA scenario - the devices were syncing correctly. I do see some errors in the server logs so posting these here for you in case you find anything that needs to be fixed:

logs-and-db.zip

@bjester
Copy link
Member

bjester commented May 15, 2024

Hi @rtibbles no issues observed while checking the above described QA scenario - the devices were syncing correctly. I do see some errors in the server logs so posting these here for you in case you find anything that needs to be fixed:

logs-and-db.zip

Looks like some DB locking errors

@bjester bjester merged commit 534aa73 into learningequality:release-v0.16.x May 15, 2024
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DEV: backend Python, databases, networking, filesystem... SIZE: medium SIZE: small TODO: needs review Waiting for review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants