-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Fix Cards with Missing Last Review Time During Database Check #4237
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
Fix Cards with Missing Last Review Time During Database Check #4237
Conversation
Co-authored-by: Luc Mcgrady <[email protected]>
Co-authored-by: user1823 <[email protected]>
| if e.button_chosen >= 1 { | ||
| if e.has_rating_and_affects_scheduling() { | ||
| last_reviewed_at = Some(e.id.as_secs()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am assuming that this loop goes from earlier revlogs to later revlogs. So, without the following code, if a card is Reset and doesn't have any subsequent rating, the last_reviewed_at will be the timestamp of the revlog before the Reset entry, which is not desired.
| } | |
| } else if e.button_chosen = 0 && e.ease_factor = 0 { | |
| last_reviewed_at = None; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the card is Reset, its type will be New and skipped in the filling process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it still makes sense to correct the output of the function because we might use it for another purpose later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I find it's better to wrap this condition in a new function because this condition is also used in other cases.
| for (card_id, last_revlog_info) in last_revlog_info { | ||
| let card = self.get_card(card_id)?; | ||
| if let Some(mut card) = card { | ||
| if card.ctype != CardType::New && card.last_review_time.is_none() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@user1823 you can check this code.
| .execute(params![mtime, usn])?; | ||
| Ok((new_cnt, other_cnt)) | ||
| let mut last_review_time_cnt = 0; | ||
| let revlog = self.get_all_revlog_entries_in_card_order()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than fetching all revlogs and calculating last review time for each card, wouldn't it be faster to fetch the revlogs of only those cards which lack last_review_time?
Something like this: (untested)
SELECT max(id), cid GROUP BY cid FROM revlog WHERE ease >= 1 AND cid IN (SELECT id FROM cards WHERE type >= 1 AND json_extract(data, '$.lrt') IS NULL)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current code is fast enough in some large collections. But I hope @dae could test it in some huge collections.
This commit introduces the `is_reset` method to the `RevlogEntry` struct, which identifies entries representing reset operations. Additionally, the scheduling logic in `memory_state.rs` and `params.rs` has been updated to utilize this new method, ensuring that reset entries are handled correctly during review scheduling.
This commit adds the `is_cramming` method to the `RevlogEntry` struct, which identifies entries representing cramming operations. The scheduling logic in `params.rs` has been updated to utilize this new method, improving the clarity and maintainability of the code.
…nctions This commit introduces a new `has_rating` method in the `RevlogEntry` struct to encapsulate the logic for checking if an entry has a rating. The scheduling logic in `params.rs` and the calculation of normal answer counts in `card.rs` have been updated to use this new method, enhancing code clarity and maintainability.
|
The tests can be fixed by adding Or, you can modify |
rslib/src/storage/card/mod.rs
Outdated
| usn: Usn, | ||
| v1_sched: bool, | ||
| ) -> Result<(usize, usize)> { | ||
| ) -> Result<(usize, usize, usize)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Struct would be better than unnamed tuple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done on 86bbc2c
| if let Some(mut card) = card { | ||
| if card.ctype != CardType::New && card.last_review_time.is_none() { | ||
| card.last_review_time = last_revlog_info.last_reviewed_at; | ||
| self.update_card(&card)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has the potential to cause data loss if run before changes on other devices have been synced. Like in #4236 (comment) we could write the changes without mtime/usn bump, but I don't think that's a good idea, as then last_review_time doesn't propagate. I think our best approach here may be calling set_schema_modified() if last_review_time_cnt > 0 (dbcheck.rs:310), forcing the user to either do a one-way sync of the changes to other devices, or revert their changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to give this a bit more thought. In the past, a forced one-way sync was appropriate for fixing some issues, but this is probably going to happen fairly frequently until people have updated all of their clients, so I'm a bit worried about the amount of one way syncs it'll cause. But perhaps I'm overthinking things. Anyone want to weigh in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done on 6947afd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert their changes.
I am assuming this means using "Download from AnkiWeb" on the next sync. Isn't this more likely to cause data loss? If there are unsynced changes on both devices and the user uses Check Database, they won't get any option to preserve their changes because Anki will force a one-way sync.
| /// The `ease_factor` should be 0 because | ||
| /// [`crate::scheduler::states::ReviewState::revlog_kind`] returns | ||
| /// `RevlogReviewKind::Filtered` when `days_late() < 0`. | ||
| pub(crate) fn is_cramming(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These helpers are a nice touch.
Co-authored-by: user1823 <[email protected]>
…ng-Database-Check
|
Thank you both. I'm still a bit worried about the extra full syncs this may cause, but I can't see a better way to handle this right now. Bumping usn without mtime won't help - the sync protocol will still treat that as newer, resulting in other changes being overwritten, so we still need the full sync so that users are aware of the conflict. |
Problem
It takes a long time to fill all existing cards'
last_review_timebecause the user needs to review them all.Source: https://forums.ankiweb.net/t/fill-the-card-last-review-time-field-when-check-database/64792
Solution
This PR adds functionality to the database check process to automatically fix cards with missing last review times by:
last_review_timerecordedlast_review_timebased on their review historyKey Changes
card_last_review_time_emptycounter to track and report fixed cardshas_rating_and_affect_scheduling()method that properly excludes manual reschedules and cramming sessionsfix_card_properties()to also repair missing last review times by cross-referencing with revlog dataBenefits
The fix is conservative and only updates cards where the data is clearly missing, ensuring no valid data is overwritten.