@@ -815,7 +815,9 @@ TEST(AppendOnlyFileStoreScanTest, TestDropStatsAfterFiltering) {
815815TEST (AppendOnlyFileStoreScanTest, TestSnapshotLiveManifestCachePath) {
816816 TimezoneGuard guard (" Asia/Shanghai" );
817817 std::string table_path = paimon::test::GetDataDir () + " /orc/append_09.db/append_09/" ;
818- auto cache = std::make_shared<LruCache>(/* max_weight=*/ 16 * 1024 * 1024 );
818+ auto cache = std::make_shared<CountingRoutingCache>(
819+ std::map<CacheKind, int64_t >{{CacheKind::MANIFEST , 16 * 1024 * 1024 },
820+ {CacheKind::SNAPSHOT_LIVE_MANIFEST , 16 * 1024 * 1024 }});
819821
820822 // First scan on snapshot 5: cache miss, entries rebuilt from all manifests.
821823 auto scan_first = BuildScan (table_path, cache, /* bucket=*/ 0 );
@@ -839,6 +841,11 @@ TEST(AppendOnlyFileStoreScanTest, TestSnapshotLiveManifestCachePath) {
839841 ASSERT_EQ (first_cache_hit, 0 );
840842 ASSERT_EQ (first_cache_misses, 1 );
841843
844+ const int64_t manifest_gets = cache->GetCount (CacheKind::MANIFEST );
845+ ASSERT_GT (manifest_gets, 0 );
846+ ASSERT_OK_AND_ASSIGN (uint64_t first_skipped,
847+ first_metrics->GetCounter (ScanMetrics::LAST_SCAN_SKIPPED_TABLE_FILES ));
848+
842849 // Second scan on the same snapshot should read the same bucket live entries from cache.
843850 auto scan_second = BuildScan (table_path, cache, /* bucket=*/ 0 );
844851 scan_second->WithSnapshot (snapshot_5);
@@ -855,9 +862,36 @@ TEST(AppendOnlyFileStoreScanTest, TestSnapshotLiveManifestCachePath) {
855862 uint64_t materialized_rows,
856863 second_metrics->GetCounter (ScanMetrics::LAST_LAZY_DECODE_MATERIALIZED_ROWS ));
857864 ASSERT_EQ (second_cache_hit, 1 );
865+ ASSERT_EQ (cache->GetCount (CacheKind::MANIFEST ), manifest_gets);
866+ ASSERT_OK_AND_ASSIGN (uint64_t second_scanned,
867+ second_metrics->GetCounter (ScanMetrics::LAST_SCANNED_MANIFESTS ));
868+ ASSERT_OK_AND_ASSIGN (uint64_t second_skipped,
869+ second_metrics->GetCounter (ScanMetrics::LAST_SCAN_SKIPPED_TABLE_FILES ));
870+ ASSERT_EQ (second_scanned, 0 );
871+ ASSERT_EQ (second_skipped, first_skipped);
858872 ASSERT_EQ (second_cache_hits, 1 );
859873 ASSERT_GE (scanned_rows, materialized_rows);
860874 ASSERT_OK (second_metrics->GetHistogramStats (ScanMetrics::SNAPSHOT_CACHE_LOAD_DURATION ));
875+
876+ // A different predicate must still filter the cached, unfiltered bucket entries.
877+ std::shared_ptr<Predicate> predicate =
878+ PredicateBuilder::Equal (0 , " f0" , FieldType::STRING , Literal (FieldType::STRING , " David" , 5 ));
879+ auto filtered = BuildScan (table_path, cache, /* bucket=*/ 0 , predicate);
880+ filtered->WithSnapshot (snapshot_5);
881+ ASSERT_OK_AND_ASSIGN (auto filtered_plan, filtered->CreatePlan ());
882+ auto expected = BuildScan (table_path, nullptr , /* bucket=*/ 0 , predicate);
883+ expected->WithSnapshot (snapshot_5);
884+ ASSERT_OK_AND_ASSIGN (auto expected_plan, expected->CreatePlan ());
885+ ASSERT_EQ (SortedFileNames (filtered_plan->Files ()), SortedFileNames (expected_plan->Files ()));
886+ ASSERT_EQ (cache->GetCount (CacheKind::MANIFEST ), manifest_gets);
887+
888+ // Eviction must rebuild from source; an exact-hit shortcut cannot survive invalidation.
889+ cache->InvalidateAll ();
890+ auto rebuilt = BuildScan (table_path, cache, /* bucket=*/ 0 );
891+ rebuilt->WithSnapshot (snapshot_5);
892+ ASSERT_OK_AND_ASSIGN (auto rebuilt_plan, rebuilt->CreatePlan ());
893+ ASSERT_EQ (first_file_names, SortedFileNames (rebuilt_plan->Files ()));
894+ ASSERT_GT (cache->GetCount (CacheKind::MANIFEST ), manifest_gets);
861895}
862896
863897TEST (AppendOnlyFileStoreScanTest, TestSnapshotLiveManifestCacheRebuildOnMiss) {
@@ -998,9 +1032,10 @@ TEST(AppendOnlyFileStoreScanTest, SnapshotCacheRebuildsCorruptValues) {
9981032 segment.PutValue <int32_t >(offset, -1 );
9991033 }
10001034 MemorySegmentOutputStream out (MemorySegmentOutputStream::DEFAULT_SEGMENT_SIZE , pool);
1001- out.WriteValue <int32_t >(corruption == " magic" ? 0 : 0x534d4543 );
1035+ out.WriteValue <int32_t >(corruption == " magic" ? 0 : 0x534d4544 );
10021036 out.WriteValue <int32_t >(1 );
10031037 out.WriteValue <int64_t >(snapshot.Id ());
1038+ out.WriteValue <int64_t >(1 );
10041039 out.WriteValue <int32_t >(1 );
10051040 ASSERT_OK (row_serializer.Serialize (row, &out));
10061041 ASSERT_EQ (out.Segments ().size (), 1 );
@@ -1078,7 +1113,11 @@ TEST(AppendOnlyFileStoreScanTest, PartitionPruningAcrossCacheModes) {
10781113 scan->partition_filter_ = counting;
10791114 ASSERT_OK_AND_ASSIGN (auto plan, scan->CreatePlan ());
10801115 ASSERT_EQ (counting->row_calls_ .load (), 2 );
1081- ASSERT_GT (counting->stats_calls_ .load (), 0 );
1116+ if (mode == 2 && scans > 0 ) {
1117+ ASSERT_EQ (counting->stats_calls_ .load (), 0 );
1118+ } else {
1119+ ASSERT_GT (counting->stats_calls_ .load (), 0 );
1120+ }
10821121 ASSERT_EQ (plan->Files ().size (), partition == 99 ? 0 : 1 );
10831122 if (partition != 99 ) {
10841123 ASSERT_EQ (plan->Files ()[0 ].Partition ().GetInt (0 ), partition);
@@ -1156,7 +1195,7 @@ TEST(AppendOnlyFileStoreScanTest, PartitionPruningSkipsExcludedMetadataInCreateP
11561195 if (mode == 2 ) {
11571196 // Seed the complete snapshot directly: rebuilding must decode every partition.
11581197 SnapshotLiveManifestEntries entries (8 );
1159- entries.Put (1 , {kept, excluded});
1198+ entries.Put (1 , {kept, excluded}, /* total_data_files= */ 2 );
11601199 ASSERT_OK (scan->StoreSnapshotLiveManifestEntries (0 , entries));
11611200 }
11621201 for (int32_t attempt = 0 ; attempt < 2 ; ++attempt) {
0 commit comments