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

Track Metadata: Fix synchronization (import/export) of file tags #4628

Merged
merged 40 commits into from
Jan 23, 2022
Merged

Track Metadata: Fix synchronization (import/export) of file tags #4628

merged 40 commits into from
Jan 23, 2022

Conversation

uklotzde
Copy link
Contributor

@uklotzde uklotzde commented Jan 16, 2022

Synchronization of file tags (import/export) is severely broken due to various reasons:

  • Using stale data from QFileInfo that reports outdated file modification time stamps
  • Not considering unknown synchronization time stamps appropriately that are created by the database migration from 2.3 to 2.4. The export is skipped then, although apparently not strictly probably due to the internal caching issues caused by QFileInfo.

Those 2 different causes made it difficult to discover the misbehavior. The new unit test ensures that this will never break again.

Copy link
Member

@daschuer daschuer left a comment

Choose a reason for hiding this comment

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

Thank you. I have left some comments.

src/track/track.cpp Outdated Show resolved Hide resolved
src/track/track.cpp Outdated Show resolved Hide resolved
src/sources/metadatasource.cpp Show resolved Hide resolved

/// The status could not be determined for whatever reason,
/// e.g. inaccessible file, ...
Undefined,
Copy link
Member

Choose a reason for hiding this comment

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

The difference between Undefined/Unknown/Void is not obvious in the using code.
How about renaming these value to
NotImported/UnknownSyncronizedTime/UnknownFileTime

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The synchronization status type should not mention files directly. It is independent of how the content of sources is stored. Files are just one kind of storage type.

As already mentioned synchronization time stamps should not be confused with raw file time stamps and we should not mix that up on this higher level of abstraction.

The names of the variants are abstract and do not need to leak implementation details from other parts of the code. Descriptions of possible use cases have deliberately been added as possible examples in the comments.

I also dislike composed terms that start with the same stem. Different kind of "unknowns" are confusing, too. It is easier to recognize distinct words.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, so what about:
SourceSyncStatus = Unsynced
SourceSyncStatus = Syncronized
SourceSyncStatus = ImportPending
SourceSyncStatus = ExportPending

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please provide a mapping to the existing values.

Copy link
Member

@daschuer daschuer Jan 16, 2022

Choose a reason for hiding this comment

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

Void = Unsynced
Syncronized = Syncronized
Outdated = ImportPending
Undefined = ExportPending
Unknown = Unknown

Copy link
Contributor Author

@uklotzde uklotzde Jan 16, 2022

Choose a reason for hiding this comment

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

My proposal is this PR. I did not receive any alternative proposals yet that I consider as an improvement.

Copy link
Member

@daschuer daschuer Jan 16, 2022

Choose a reason for hiding this comment

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

For my feeling my recent proposal follows your demand in #4628 (comment)
My demand is to have more descriptive names than the ambiguous Undefined/Unknown/Void.
What would be an alternative for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The variant names are recognizable and they were helpful while writing the different test cases.

They represent a state and are not directly related to any import/export activity as you suggested.

Copy link
Member

Choose a reason for hiding this comment

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

If you really insist to keep these empty and interchangeable words Undefined/Unknown/Void and like to rely on out of sight comments, I'll give up. It is a pity that we have not found a compromise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Naming can be changed at any time. But I refuse to overload this PR with more unrelated changes.

@uklotzde uklotzde changed the title Track Metadata: Fix synchronization (import/export) of file tags [WiP] Track Metadata: Fix synchronization (import/export) of file tags Jan 18, 2022
@uklotzde uklotzde marked this pull request as draft January 18, 2022 14:17
@uklotzde uklotzde changed the title [WiP] Track Metadata: Fix synchronization (import/export) of file tags Track Metadata: Fix synchronization (import/export) of file tags Jan 18, 2022
@uklotzde uklotzde marked this pull request as ready for review January 18, 2022 17:17
@uklotzde
Copy link
Contributor Author

uklotzde commented Jan 18, 2022

I hope that c72115f fixes the sporadic CI failures. One sleep might have been missing. The dependency on uncontrollable side effects is unfortunate.

@Holzhaus
Copy link
Member

Now clang-tidy fails.

daschuer added a commit that referenced this pull request Jan 18, 2022
TrackDAO: Refactorings from debugging #4628
@daschuer
Copy link
Member

rror: /home/runner/work/mixxx/mixxx/src/library/trackcollectionmanager.cpp:333:42: error: this statement may fall through [-Werror=implicit-fallthrough=]
  333 |             pTrack->markForMetadataExport();

@uklotzde
Copy link
Contributor Author

uklotzde commented Jan 19, 2022

Fore my feeling the new test class overshoots our goal by far. We have no interest in tests, testing file timing in a separate testing calls. The test should test the productive code. In the productive code we have a > operator for testing for outdated files. We have no case where we write a file and read it back immediately. So the implemented ms resolution is not required.

The only reason I can consider in real live where this is relevant is a concurrent write to the same file form Mixxx and another Tool. Here is the result undefined anyway.

It should be sufficient to test this with a reasonable old file. So you may consider to reintroduce the changing the file time directly and set it to a age of one hour or such. This way we can get rid of all the tests failures related to file time resolution and caching.

I also prefer to go back to a procedural test body that is closer to the implementations of other tests. This is way easier to read instead of your deeply nested test classes where the control flow is hard to track, especially because an implicit run time measurement is involved.

These extensive tests with all their combinatorics have revealed many issues.

@uklotzde
Copy link
Contributor Author

We should restart the build jobs repeatedly to see if there are still timing issues on Ubuntu with the minimum delay of 1 ms between file modifications.

All timing code in Mixxx and the tests should only compare time stamps that have been generated by the file system as the single source of truth. I am not aware that we missed any occurrence that uses time stamps from a second, inconsistent source.

@Be-ing
Copy link
Contributor

Be-ing commented Jan 19, 2022

Manually restarting GitHub Actions jobs will cancel the old run. You can hack around this by repeatedly doing git commit --amend and force pushing to create jobs testing identical code with different commit hashes.

@uklotzde
Copy link
Contributor Author

uklotzde commented Jan 19, 2022

Manually restarting GitHub Actions jobs will cancel the old run. You can hack around this by repeatedly doing git commit --amend and force pushing to create jobs testing identical code with different commit hashes.

Restarting works as expected and the tests have been rerun as intended. We want them to fail, not to succeed.

@uklotzde
Copy link
Contributor Author

ed15311 will hopefully fix the Ubuntu issues.

78ecbac saves 1 sleep operation per test case.

@daschuer
Copy link
Member

daschuer commented Jan 20, 2022

It is still failing locally in rare cases:

info [0x55bd0389efd0] TrackCollectionManager - External collections are disabled in test mode
info [0x55bd0389efd0] TrackCollectionManager - Library scanner is disabled in test mode
info [0x55bd0389efd0] SeratoMarkers2 - Reading bpmlock state from 'Serato Markers2' tag data...
info [0x55bd0389efd0] SeratoMarkers2 - Reading track color from 'Serato Markers2' tag data...
warning [0x55bd0389efd0] Track - Exporting and overwriting metadata into file "/tmp/mixxx-test-NjCcZd/cover-test-jpg.mp3" even though the file has been modified at QDateTime(2022-01-20 11:29:18.314 UTC Qt::UTC) after the last library synchronization at QDateTime(2022-01-20 11:29:18.306 UTC Qt::UTC)
/home/sperry/workspace/2.3/src/test/libraryfilesynctest.cpp:253: Failure
Value of: !fileLastModifiedAfter.isValid() || fileLastModifiedAfter > fileLastModifiedBefore
  Actual: false
Expected: true
info [0x55bd0389efd0] TrackCollection - Disconnecting database
info [0x55bd0389efd0] GlobalTrackCache - Destroying instance
[  FAILED  ] LibraryFileWithMetadataSyncStatusOutdatedTest.saveTrackMetadataWithSyncEnabled (24 ms)
[----------] 1 test from LibraryFileWithMetadataSyncStatusOutdatedTest (24 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (24 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] LibraryFileWithMetadataSyncStatusOutdatedTest.saveTrackMetadataWithSyncEnabled

 1 FAILED TEST

@uklotzde
Copy link
Contributor Author

uklotzde commented Jan 20, 2022

It is still failing locally in rare cases:

info [0x55bd0389efd0] TrackCollectionManager - External collections are disabled in test mode
info [0x55bd0389efd0] TrackCollectionManager - Library scanner is disabled in test mode
info [0x55bd0389efd0] SeratoMarkers2 - Reading bpmlock state from 'Serato Markers2' tag data...
info [0x55bd0389efd0] SeratoMarkers2 - Reading track color from 'Serato Markers2' tag data...
warning [0x55bd0389efd0] Track - Exporting and overwriting metadata into file "/tmp/mixxx-test-NjCcZd/cover-test-jpg.mp3" even though the file has been modified at QDateTime(2022-01-20 11:29:18.314 UTC Qt::UTC) after the last library synchronization at QDateTime(2022-01-20 11:29:18.306 UTC Qt::UTC)
/home/sperry/workspace/2.3/src/test/libraryfilesynctest.cpp:253: Failure
Value of: !fileLastModifiedAfter.isValid() || fileLastModifiedAfter > fileLastModifiedBefore
  Actual: false
Expected: true
info [0x55bd0389efd0] TrackCollection - Disconnecting database
info [0x55bd0389efd0] GlobalTrackCache - Destroying instance
[  FAILED  ] LibraryFileWithMetadataSyncStatusOutdatedTest.saveTrackMetadataWithSyncEnabled (24 ms)
[----------] 1 test from LibraryFileWithMetadataSyncStatusOutdatedTest (24 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (24 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] LibraryFileWithMetadataSyncStatusOutdatedTest.saveTrackMetadataWithSyncEnabled

 1 FAILED TEST

Unfortunately we cannot loop our track saving code until a different time stamp for the file is generated when exporting metadata. Then we need to increase the sleep time until the tests succeed reliably.

I am running the tests on btrfs and tmpfs (used by QTemporaryDir). They succeed even if I comment out the sleep.

eb0c83f

src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
ASSERT_EQ(1, pTrack.use_count());
const auto trackId = pTrack->getId();
ASSERT_TRUE(trackId.isValid());
ASSERT_TRUE(pTrack->isDirty());
Copy link
Member

Choose a reason for hiding this comment

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

Is this the case? This does not match to the comment below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean?

src/test/libraryfilesynctest.cpp Outdated Show resolved Hide resolved
@daschuer
Copy link
Member

The latest commit works here. However I would prefer a more robust solution by just setting the file time to an reasonable old value on creation.

@uklotzde
Copy link
Contributor Author

uklotzde commented Jan 20, 2022

The latest commit works here. However I would prefer a more robust solution by just setting the file time to an reasonable old value on creation.

That would be inconsistent with the implicit update of the modification time stamp when saving the file. We don't know anything about the relation of the different clocks and should rely on a single source of truth.

I have already burned my fingers and will not touch it again!!

@daschuer
Copy link
Member

Original we had in one case a bussy loop that finds exactly the minimum difference that is considered as such. But you had no solution for setting the file to a newer value.

Now you have increased the sleep time to 2 ms, a wild guess that works on my device which makes the busy loop obsolete, because the 2 is used there as well.

This mean you original demand to test with them I um difference does no longer work anyway.

I think a much better solution is to creat the file, read the file time and sibstract 1 and write it back.

The same can be done when advancing the file time read it add one and write it back. This way it should be rock solid.

@uklotzde
Copy link
Contributor Author

uklotzde commented Jan 20, 2022

Original we had in one case a bussy loop that finds exactly the minimum difference that is considered as such. But you had no solution for setting the file to a newer value.

Now you have increased the sleep time to 2 ms, a wild guess that works on my device which makes the busy loop obsolete, because the 2 is used there as well.

This mean you original demand to test with them I um difference does no longer work anyway.

I think a much better solution is to creat the file, read the file time and sibstract 1 and write it back.

The same can be done when advancing the file time read it add one and write it back. This way it should be rock solid.

Thanks. That was actually a good idea. The current test design has proven to be solid as this change was easy to integrate. Though somehow also tricky, I added comments.

3a44c83

@daschuer
Copy link
Member

Now it is working reliable. Thank you. I am sorry for you burned fingers.

@daschuer daschuer merged commit cf88944 into mixxxdj:main Jan 23, 2022
@uklotzde uklotzde deleted the trackrecord branch January 23, 2022 10:36
@ronso0 ronso0 added this to the 2.4.0 milestone Aug 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants