Scope filters should list read-write partitions last
@bjester 已经在做这个了。
开始于 2026年10月21日。
评估
这个 Issue 还没有评估数据。
描述
❌ 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_8439SingleUserSyncRegressionsTestCase::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 thatScopebuilds 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_filterlists the read-only partitions, then the read-write partitions.Scope.write_filterlists 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
Scopefilters directly get a usable deserialization order without extra code. - Kolibri does not need to reorder the read filter of the
single-userscope.
Deliverables & Contracts
Scope.read_filterandScope.write_filterlist the read-write partitions last.- The docstrings of
Scopeand of the stream source describe the order contract. - The
0.9.0section ofCHANGELOG.mdrecords the new order.
Acceptance Criteria
- A test builds a
Scopefrom 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-usershape 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_146andtest_single_user_course_syncing. -
make testpasses on SQLite and PostgreSQL.
Technical Pointers & Architecture
- Target Components / Context:
Scope.__init__inmorango/models/certificates.py.MorangoSource.prefix_conditionsinmorango/sync/stream/source.pydocuments 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.filterstoresstr(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, andFilter.__eq__, whichmorango/sync/context.pyand Kolibri use. Check for any other code that compares filter strings.
Notes & Tradeoffs
- Dependencies: Related to #392. Do this before
0.9.0ships, because0.9.0is the first release in which the filter order matters. - Testing Requirements:
tests/testapp/tests/test_api.pytakes the first partition of aread_filterwith.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
Scopecombines 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.
- Kolibri reorders the filter itself. This works, and the Kolibri integration tests pass with it, but each implementer must know how
- 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
环境准备
- 提供 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
learningequality/morango 的其他 Issue
-
DEV: dev-ops DEV: distributions DOCS: developer
难度 1/5 1-3 小时 新手友好度 78/100
learningequality/morango#387 ·
-
DEV: backend DEV: dev-ops TAG: unit tests
难度 2/5 1-3 小时 新手友好度 70/100
learningequality/morango#385 ·
-
DEV: backend P0 - critical TAG: tech update / debt TAG: unit tests
难度 2/5 1-3 小时 新手友好度 72/100
learningequality/morango#364 ·
-
Remove the deferred FK validation API and unused backend bulk helpers可能已有人在做 @bjester 于 3 天前认领。 未关闭TAG: tech update / debt
learningequality/morango#396 · 已指派 1 人 ·
-
dependencies DEV: dev-ops TAG: dependencies
难度 2/5 1-3 小时 新手友好度 1/100
learningequality/morango#395 ·
查看 learningequality/morango 的全部 Issue
相似的 Issue
-
good first issue priority: low size: XS testing
难度 1/5 1-3 小时 新手友好度 86/100
cubrid-lab/cubrid-mcp-server#385 ·
维护者通常 1 天内回复
-
good first issue priority: low size: S testing
难度 2/5 1-3 小时 新手友好度 85/100
cubrid-lab/cubrid-cookbook-python#447 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
ArduCAM/Arducam_tof_camera#125 ·
-
area:curator enhancement
难度 2/5 1-3 小时 新手友好度 74/100
CassioDalla/Scrinalia#150 ·
维护者通常 1 天内回复
-
documentation good first issue
难度 2/5 1-3 小时 新手友好度 72/100