Do the two servers write BaseItems.Data keys in the same order? #150
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Batch D stood a pair up by hand and
diff-dbreported two differences that havenothing to do with the pictures it was there to measure. Neither is filed as a
defect, because the way the pair was built could account for both — but if the
first one is real it is much larger than where it was seen.
BaseItems.Datadiffers by JSON key order on the three library rows. Samecontent; ours writes the keys sorted (
CollectionType, Height, IsHD, …) andupstream writes them in insertion order (
PhysicalLocationsList, PhysicalFolderIds, CollectionType, …). The existingDataexemption masks onlyDateLastSaved, anddiff-dbcompares literals — so if ours sorts everywhere,this is a difference on every row whose blob carries more than one key, not a
difference on libraries.
Three
AncestorIdsrows ours files and upstream does not: each library filedunder the user root folder as an ancestor.
Why it is not filed as a defect
The pair was hand-built rather than stood up through
cargo xtask reference, andthat upstream received a
POST /Library/VirtualFolders/LibraryOptionsours neversaw — so the library rows may have been written by a different upstream path than
a normal bootstrap uses.
declare_librarieswrites these rows on every start,which is what makes both look real rather than incidental, but that is an
argument and not a measurement.
How to know it is done
Reproduce through
cargo xtask reference— a bootstrap on each side, thendiff-db. Either the difference is there, in which case the key-order half needsits blast radius established before anything else, or it is not, in which case
this closes with the note that a hand-built pair diffs differently from a
provisioned one, which is itself worth knowing.
House rules
Tests run under
cargo nextest; upstream's source is the checkout inupstream/at the pin. The reference on:8096is read-only, andStudios=,Artists=andAlbums=are writes. Anything needing a write getsits own container from the pinned image over a
cp -acopy of a snapshot, on itsown ports. Rebuilding the reference redraws the account and server ids that
scenarios and
config.tomlname.Both halves reproduce, neither the way the hand-built pair read them, and both
are fixed. The reference snapshot's own
jellyfin.dbis the measurement —reference/snapshots/default/config/data/jellyfin.dbis what a provisionedupstream wrote, so no pair had to be stood up.
Key order: real, and not everywhere
Ours does not sort every blob.
Write::derived_dataalready writes memberby member through
item::ordered_object, in upstream's declaration order, andthe module that owns the rule says so:
Exactly two writers took the top-level object from
serde_jsonanyway —json!({…}).to_string()renders aBTreeMap, so both came out sorted:scan/library.rs::library_data, theCollectionFolderblob.declare_librarieswrites it on every start, so it is a difference on every library row.
rows/item.rs::Sharing::blob, a playlist's — including the two properties ofeach entry of
Shares, which sorting inverts.box_set_datawas already usingordered_object. So the blast radius islibrary rows plus playlist rows, not every row whose blob carries more than one
key.
What upstream wrote for the reference's movie library:
CollectionFolderdeclares the first three;FolderdeclaresIsRoot;BaseItemdeclares the rest.DateLastSavedsits betweenHeightandRemoteTrailersand is what the existingDataexemption masks —json_removeruns over both sides and preserves the order of everything else, which is why
the order difference was not hidden with it.
Ours is now pinned to that text, upstream's own
PhysicalFolderIdshashincluded, so the test checks what the blob says as well as the order it says it
in.
AncestorIds: real, and the other rowThe hand-built pair read the three extra rows as each library filed under the
user root folder. A provisioned upstream files exactly that:
One
AncestorIdsrow perCollectionFolder, naming theUserRootFolder— andnone naming the root. Ours filed
[user_root, root], so the extra row perlibrary was the one naming the aggregate root, not the user root. A library
hangs from Media Folders, which hangs from nothing, so its chain stops there;
the folder the library points at is the one that hangs from the root, and it
already did.
Nothing could fail on either, so both shapes are now asserted:
a_librarys_blob_is_written_in_the_order_upstream_wrote_it,a_playlists_blob_is_written_in_the_order_upstream_declares_it,a_library_is_an_ancestors_chain_of_one_and_its_folder_another.Left behind
Upstream marks
Folder.LinkedChildren[JsonIgnore]at the pin — links live inthe table — and ours still writes
"LinkedChildren":[]into a playlist's blob.That is a difference in what the blob says rather than in the order it says it
in, and no fixture holds a playlist row, so it is not touched here. Worth its
own issue.
0afe805,9ac0217.