Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Scope filters should list read-write partitions last

未关闭
#398 0 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

@bjester 已经在做这个了。

开始于 2026年10月21日。

评估

这个 Issue 还没有评估数据。

描述

DEV: backend P0 - critical TAG: tech update / debt

❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.

Overview

Make the read and write filters of a Scope list the read-write partitions last, so that deserialization in filter order puts shared records before the records that reference them.

Background & Motivation

#392 streams deserialization one partition prefix at a time, in the order of the sync filter. A record whose FK target is in a later partition gets a deserialization error, because the target is not in the app table yet. The legacy deserialization looped model by model across all partitions, so the order of the filter did not matter before 0.9.0.

The contract in #392 is that the caller gives the partitions in a usable order. But callers do not always write the filter. Kolibri passes Scope.read_filter and Scope.write_filter directly as its pull and push filters, and Scope builds them as the read-write partitions, then the read-only or write-only partitions:

Scope Kolibri template order Scope.read_filter order
single-user read: allusers-ro, user-ro:U. read-write: user-rw:U user-rw:U, allusers-ro, user-ro:U
full-facility read-write: D D

In the single-user scope, the logs are in user-rw:U and the FacilityUser is in user-ro:U. Morango deserializes the logs first, and they fail on a device that does not have the user yet. With #392 installed, two of Kolibri's Morango integration tests failed for this reason:

  • SingleUserSyncRegressionsTestCase::test_kolibri_issue_8439
  • SingleUserSyncRegressionsTestCase::test_issue_fixed_in_morango_pull_146

Both tests passed after Kolibri reordered the read filter itself, with the read-only partitions first. That fix puts a Morango detail in Kolibri, and every other implementer would need the same fix.

User Story / Use Case

As a developer of an app that syncs with Morango,
I want the filters that Scope builds from my scope definition to keep a usable order,
So that I control the deserialization order through my templates, and do not have to reorder Morango's filters.

Description & Expected Outcomes
  • Scope.read_filter lists the read-only partitions, then the read-write partitions.
  • Scope.write_filter lists the write-only partitions, then the read-write partitions.
  • Within each template, the partitions keep the order of the template.
  • The order the filter must have for deserialization is documented, so implementers know how to order their templates.

Read-write partitions usually hold the device's own records, such as a user's logs. Those records usually reference shared records in the read-only partitions, such as the user, the facility and the classrooms. The reverse is rare. This order is a convention, not a guarantee. A read-only record that references a read-write record still fails, and Morango cannot detect this case, because it knows the dependencies between models but not between partitions.

Value Add & Impact
  • Implementers that pass Scope filters directly get a usable deserialization order without extra code.
  • Kolibri does not need to reorder the read filter of the single-user scope.

Deliverables & Contracts

  • Scope.read_filter and Scope.write_filter list the read-write partitions last.
  • The docstrings of Scope and of the stream source describe the order contract.
  • The 0.9.0 section of CHANGELOG.md records the new order.
Acceptance Criteria
  • A test builds a Scope from templates with read-only, write-only and read-write partitions, and checks the order of both filters.
  • A deserialization test with a filter in the single-user shape deserializes a record before the records that reference it.
  • Kolibri's Morango integration tests pass without Kolibri reordering the read filter, including test_kolibri_issue_8439, test_issue_fixed_in_morango_pull_146 and test_single_user_course_syncing.
  • make test passes on SQLite and PostgreSQL.

Technical Pointers & Architecture

  • Target Components / Context: Scope.__init__ in morango/models/certificates.py. MorangoSource.prefix_conditions in morango/sync/stream/source.py documents the order contract.
  • Related Patterns: Filter.__add__ keeps the order of both operands and removes duplicates.
  • Data Model & Schema Considerations: No model fields or migrations change. TransferSession.filter stores str(filter), so new transfer sessions store the partitions in the new order.
  • Resilience & Failure Modes: The comparisons we found do not depend on order. These are Filter.is_subset_of, which the server uses to validate a requested filter, and Filter.__eq__, which morango/sync/context.py and Kolibri use. Check for any other code that compares filter strings.

Notes & Tradeoffs

  • Dependencies: Related to #392. Do this before 0.9.0 ships, because 0.9.0 is the first release in which the filter order matters.
  • Testing Requirements: tests/testapp/tests/test_api.py takes the first partition of a read_filter with .split()[0]. Its scope has no read-write partitions, so the change does not affect it, but check similar tests.
  • Tradeoffs & Alternatives Considered:
    • Kolibri reorders the filter itself. This works, and the Kolibri integration tests pass with it, but each implementer must know how Scope combines the templates.
    • Morango sorts the partition prefixes. #392 tried this before release. It broke test_single_user_course_syncing, where the classroom partitions sort before the user partitions they reference.
    • Morango deserializes model by model across all partitions, with one query that combines the prefixes, as the legacy code did. This makes the order irrelevant, but we do not want to combine many prefixes into one query expression.
  • Compatibility & Migrations: Devices on older Morango versions do not depend on the filter order, so mixed-version syncs are not affected.

Metadata

Complexity: Low
Target Branch: release-v0.9.x
Labels: TAG: tech update / debt

AI Usage

Drafted with Claude Code while we investigated Kolibri integration test failures against #392. Claude traced the failures to the order of Scope.read_filter, and tested a Kolibri-side reorder that made the tests pass. I rejected a Morango-side fix that combined the prefixes into one query, and chose to track the Scope change in this issue instead of in #392.

主要语言
Python
星标
15
派生
23
平均合并
2 天 8 小时
30 天内合并 PR
5

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

learningequality/morango 的其他 Issue

查看 learningequality/morango 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。