beetbox / beets

music library manager and MusicBrainz tagger
http://beets.io/
MIT License
12.71k stars 1.81k forks source link

Beets History Logging #1392

Open ab623 opened 9 years ago

ab623 commented 9 years ago

Hi there.

So I've been looking to implement a beets work history log. This will essentially log all the actions beets completes, along with any supplementary information. A user then is able to see this via the beet interface and have a more granular view of the updates beets makes and to what. It will in the future allow the user to rollback changes (within reason) to a specific point in time. This means users can "test" commands, and commit changes. In addition if a feature to run beets commands in the background is implemented, a log of work can be recorded.

I've penned up an initial design to this functionality, with the hopes others can contribute and we can work as a community to see this implemented. This is a work in progress so anyone can suggest changes, etc.

I think this development should happen in 2 phases. They are as follows:

I believe the following columns will be required for the new history database.

The logs will be able to log the following actions:

File Actions

Library Actions

General Actions

Each of these actions can and should be logged, and with each (except delete) a reversal process exists, which can be used to rollback changes.

With each action we would also needs to store a serialised version of the item, before the change is made. e.g. before a beet write writes to a file, we should log the full data of the item, and then run the command. This means when we rollback, we have have the information to rollback to.

> Config

For this phase I see only the following configuration items being relevant at the minute. Happy to accept more

The process in which this would work would be simple. For each write to the database, the required information is stored. As the transaction class is abstracted, this write to the history can be implemented in a central location. It is important to note that there will be some slowdown in writing due to each request being logged. However this slowdown, is the tradeoff for implementing such feature.

As the logs go in, a process will be required to remove old logs, via a SQL command.

> UI

I believe the history UI should be fairly simple, called with something similar to beet history it will display a table of information, which will be pretty much a dump of the new history table. This will allow the user to see what beets has done.

Phase 2

I wont discuss much of phase 2 at the moment, but the premise is to effectively pass beets a number of the history item, and beets will start a process to run though every log from the present back to the specified number, and reverse every action to get beets back to that specific point in time.

Further details and design of this will be created at a later point.

Conclusion

I would like to hear all of your thought. Also there are some architectural challenges to face, like how we can add another table to beets but separate it from the library. Right now item and album are subclasses of Database. And we really need to figure out the best method of sorting and accessing the logs.

sampsyo commented 9 years ago

Interesting idea—I can see the use case.

Since versioning and rollback is such a complex concern, I would love to see the strategy here be based on prior work. Database versioning is a problem closely related to replication, which is in turn useful for beets' network API (see #736). It's common for web APIs to sync via revision numbers: essentially the same concept that's necessary here.

I don't think there's any off-the-shelf library we can use, but it would at least be good to look at other people's approaches: for example, here's a useful article about database revisions in the form of history tables. The main difference here is that we'd need to track filesystem changes in concert with the database itself.

Overall: A good first step here would be to prototype the basic versioning system for the database. It should be able to do this with minimal API disruption across the codebase, and it should add the (theoretical) ability to view the database's complete state at any revision in the past with something like beet ls -r5 or something. Once that proof of concept seems feasible and the abstractions are right, we can start worrying about the practical concerns.

ab623 commented 9 years ago

I know the idea is ambitious, but I think beets can be taken to never levels by implementing these types of features.

I like your idea of basing it off prior work, however I think there might be a slight variance in how we store the information.

Due to beets nature, if the 2 items, went thought 10 changes, we cant allow one item to revert back to revision 5, the entire database and field structure would have to reverse back to revision 5 for all items. This is to make the implementation MUCH simpler. reverting individual changes would be a nightmare to keep track of.

As I mentioned, and you agree with :), is we implement this in 2 phases. With phase 1 being the implementation of the revision logging/history feature. Is it best to develop this feature in a new branch?

sampsyo commented 9 years ago

Yes, whole database revisions seem like the way to go.

And also yes: a branch would be the right place to play with this. In the design, the overriding concern should be simplicity: the feature should be as orthogonal as possible to the other functionality in dbcore and, ideally, should not fundamentally change the Library API when doing ordinary (non-version-related) manipulations.

goretkin commented 3 years ago

I should be sorry to revive an old issue, but it's still open, so I figured I could give it a chance. If I understand correctly, sqlite is used to persist all of the music library state. Does anyone use something like https://ongardie.net/blog/sqlite-in-git/ or https://stackoverflow.com/a/38271631/415404 to version the sqlite database?

The diffs would be less useful (less "semantic") than a bespoke schema for versioning, or logging all actions done on the database, with beets-specific metadata. But it would be easier to implement since it's a beets-agnostic solution.