fix(follow): keep sync status rows for the sync digest

A hand-only digest used to move the cursor past all status rows.
It now stops at the last by_user change, so the sync digest still
gets its status detail lines.
This commit is contained in:
lobo committed 2026-10-13 11:15:10 +02:00
1 parent c6bfea1976
commit cdfbb4a863
3 files changed
+29 -1

No files matched your search

@@ -89,6 +89,17 @@ public class FollowDigestRepository {
return max != null ? max : 0; return max != null ? max : 0;
} }
/**
* The newest hand change after {@code afterId}, or {@code afterId} when there is none. A hand-only digest stops
* there, so the status rows of a sync stay for the digest of that sync.
*/
public long maxHandStatusChangeId(long userId, long afterId) {
Long max = jdbc.queryForObject("select max(id) from user_game_status_change "
+ "where user_id = :userId and by_user and id > :afterId",
user(userId).addValue("afterId", afterId), Long.class);
return max != null ? max : afterId;
}
/** /**
* The changes the user made by hand: no old status = a game added by hand, else a status change. * The changes the user made by hand: no old status = a game added by hand, else a status change.
*/ */
@@ -69,7 +69,8 @@ public class FollowDigestService {
return; return;
} }
Cursor cursor = locked.get(); Cursor cursor = locked.get();
long upTo = digestRepository.maxStatusChangeId(userId); long upTo = withSync ? digestRepository.maxStatusChangeId(userId)
: digestRepository.maxHandStatusChangeId(userId, cursor.statusChangeId());
Digest digest = digestRepository.countHand(userId, cursor.statusChangeId(), upTo); Digest digest = digestRepository.countHand(userId, cursor.statusChangeId(), upTo);
Instant syncStartedAt = cursor.syncStartedAt(); Instant syncStartedAt = cursor.syncStartedAt();
long wishlistUpTo = cursor.wishlistChangeId(); long wishlistUpTo = cursor.wishlistChangeId();
@@ -439,4 +439,20 @@ class FollowDigestServiceTest extends AbstractIntegrationTest {
assertThat(jdbc.queryForObject("select count(*) from inbox_note where achievements = 7 and new_games = 2", assertThat(jdbc.queryForObject("select count(*) from inbox_note where achievements = 7 and new_games = 2",
Integer.class)).isEqualTo(2); Integer.class)).isEqualTo(2);
} }
@Test
void handJobBeforeSync_LeavesTheSyncStatusRowsForTheSyncDigest() {
handGame(920_001L, 30);
added(1);
moved(FIRST_APPID + 1);
service.digestHandChanges();
event();
assertThat(details(FOLLOWER_ONE)).extracting(row -> row.get("kind"), row -> row.get("game_name"))
.containsExactlyInAnyOrder(org.assertj.core.groups.Tuple.tuple("NEW_GAME", "Hand"),
org.assertj.core.groups.Tuple.tuple("NEW_GAME", "Game 1"),
org.assertj.core.groups.Tuple.tuple("STATUS", "Game 1"));
assertThat(notes()).extracting(note -> note.get("status_changes")).containsOnly(0, 1);
}
} }