001package gudusoft.gsqlparser.sqlenv.catalog; 002 003import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 004import gudusoft.gsqlparser.sqlenv.TSQLSchemaObject; 005 006import java.util.HashMap; 007import java.util.Map; 008import java.util.concurrent.atomic.AtomicLong; 009 010/** 011 * 差异日志(记录新旧实现的差异) 012 * 013 * <p>用于 Phase 1-2 期间验证新实现的正确性。 014 * 015 * @since 3.2.0 (Phase 1) 016 */ 017public class DifferenceLogger { 018 019 private final AtomicLong legacyPutErrorCount = new AtomicLong(0); 020 private final AtomicLong newPutErrorCount = new AtomicLong(0); 021 private final AtomicLong searchDiffCount = new AtomicLong(0); 022 private final AtomicLong searchMissCount = new AtomicLong(0); 023 private final AtomicLong newSearchErrorCount = new AtomicLong(0); 024 025 // Sampling rate for logging (to avoid log explosion) 026 private static final int LOG_SAMPLE_RATE = 100; 027 028 /** 029 * 记录旧实现的 put 错误 030 * 031 * @param object the schema object 032 * @param e the exception 033 */ 034 public void logLegacyPutError(TSQLSchemaObject object, Throwable e) { 035 long count = legacyPutErrorCount.incrementAndGet(); 036 System.err.println("[Legacy] Put error: object=" + object + ", error=" + e.getMessage()); 037 } 038 039 /** 040 * 记录新实现的 put 错误 041 * 042 * @param object the schema object 043 * @param e the exception 044 */ 045 public void logNewPutError(TSQLSchemaObject object, Throwable e) { 046 long count = newPutErrorCount.incrementAndGet(); 047 048 // Log first occurrence and every 100th error 049 if (count == 1 || count % LOG_SAMPLE_RATE == 0) { 050 System.err.println("[CatalogStore] Put error (count=" + count + "): object=" + 051 object + ", error=" + e.getMessage()); 052 } 053 054 // Log stack trace for serious errors 055 if (count % (LOG_SAMPLE_RATE * 10) == 0) { 056 System.err.println("[CatalogStore] Put error count reached: " + count); 057 e.printStackTrace(); 058 } 059 } 060 061 /** 062 * 记录搜索结果差异 063 * 064 * @param catalog catalog name 065 * @param schema schema name 066 * @param objectName object name 067 * @param type object type 068 * @param legacyResult legacy implementation result 069 * @param newResult new implementation result 070 */ 071 public void logSearchDifference(String catalog, String schema, String objectName, 072 ESQLDataObjectType type, 073 TSQLSchemaObject legacyResult, 074 TSQLSchemaObject newResult) { 075 long count = searchDiffCount.incrementAndGet(); 076 077 // Log first occurrence and every 100th difference 078 if (count == 1 || count % LOG_SAMPLE_RATE == 0) { 079 System.err.println("[Difference] Search mismatch (count=" + count + "): " + 080 "catalog=" + catalog + ", schema=" + schema + ", object=" + objectName + 081 ", type=" + type + ", legacy=" + legacyResult + ", new=" + newResult); 082 } 083 084 // Alert on significant differences 085 if (count % (LOG_SAMPLE_RATE * 10) == 0) { 086 System.err.println("[Difference] Search mismatch count reached: " + count); 087 } 088 } 089 090 /** 091 * 记录搜索 miss(新实现未找到对象) 092 * 093 * @param catalog catalog name 094 * @param schema schema name 095 * @param objectName object name 096 * @param type object type 097 */ 098 public void logSearchMiss(String catalog, String schema, String objectName, 099 ESQLDataObjectType type) { 100 long count = searchMissCount.incrementAndGet(); 101 102 // Log first occurrence and every 100th miss 103 if (count == 1 || count % LOG_SAMPLE_RATE == 0) { 104 System.err.println("[CatalogStore] Search miss (count=" + count + "): " + 105 "catalog=" + catalog + ", schema=" + schema + ", object=" + objectName + 106 ", type=" + type); 107 } 108 } 109 110 /** 111 * 记录新实现的 search 错误 112 * 113 * @param catalog catalog name 114 * @param schema schema name 115 * @param objectName object name 116 * @param type object type 117 * @param e the exception 118 */ 119 public void logNewSearchError(String catalog, String schema, String objectName, 120 ESQLDataObjectType type, Throwable e) { 121 long count = newSearchErrorCount.incrementAndGet(); 122 123 // Log first occurrence and every 100th error 124 if (count == 1 || count % LOG_SAMPLE_RATE == 0) { 125 System.err.println("[CatalogStore] Search error (count=" + count + "): " + 126 "catalog=" + catalog + ", schema=" + schema + ", object=" + objectName + 127 ", type=" + type + ", error=" + e.getMessage()); 128 } 129 130 // Log stack trace for serious errors 131 if (count % (LOG_SAMPLE_RATE * 10) == 0) { 132 System.err.println("[CatalogStore] Search error count reached: " + count); 133 e.printStackTrace(); 134 } 135 } 136 137 /** 138 * 获取统计信息 139 * 140 * @return statistics map 141 */ 142 public Map<String, Long> getStatistics() { 143 Map<String, Long> stats = new HashMap<>(); 144 stats.put("legacyPutErrorCount", legacyPutErrorCount.get()); 145 stats.put("newPutErrorCount", newPutErrorCount.get()); 146 stats.put("searchDiffCount", searchDiffCount.get()); 147 stats.put("searchMissCount", searchMissCount.get()); 148 stats.put("newSearchErrorCount", newSearchErrorCount.get()); 149 return stats; 150 } 151 152 /** 153 * 重置统计 154 */ 155 public void reset() { 156 legacyPutErrorCount.set(0); 157 newPutErrorCount.set(0); 158 searchDiffCount.set(0); 159 searchMissCount.set(0); 160 newSearchErrorCount.set(0); 161 } 162 163 /** 164 * 生成统计报告 165 * 166 * @return statistics report string 167 */ 168 public String generateReport() { 169 StringBuilder sb = new StringBuilder(); 170 sb.append("DifferenceLogger Statistics:\n"); 171 sb.append("====================================\n"); 172 sb.append("Legacy Put Errors: ").append(legacyPutErrorCount.get()).append("\n"); 173 sb.append("New Put Errors: ").append(newPutErrorCount.get()).append("\n"); 174 sb.append("Search Differences: ").append(searchDiffCount.get()).append("\n"); 175 sb.append("Search Misses: ").append(searchMissCount.get()).append("\n"); 176 sb.append("New Search Errors: ").append(newSearchErrorCount.get()).append("\n"); 177 sb.append("====================================\n"); 178 179 // Calculate quality metrics 180 long totalSearches = searchDiffCount.get() + searchMissCount.get(); 181 if (totalSearches > 0) { 182 double consistency = 100.0 * (1.0 - (double)searchDiffCount.get() / totalSearches); 183 double completeness = 100.0 * (1.0 - (double)searchMissCount.get() / totalSearches); 184 sb.append("Consistency: ").append(String.format("%.2f%%", consistency)).append("\n"); 185 sb.append("Completeness: ").append(String.format("%.2f%%", completeness)).append("\n"); 186 } 187 188 return sb.toString(); 189 } 190 191 /** 192 * 检查是否有严重错误(用于测试) 193 * 194 * @return true if there are serious errors 195 */ 196 public boolean hasSeriousErrors() { 197 // Legacy put errors are always serious 198 if (legacyPutErrorCount.get() > 0) { 199 return true; 200 } 201 202 // New put error rate > 1% is serious 203 if (newPutErrorCount.get() > 0) { 204 // This is conservative - in production we'd need actual total count 205 return true; 206 } 207 208 return false; 209 } 210 211 /** 212 * 获取新实现的错误率 213 * 214 * @param totalOperations total number of operations 215 * @return error rate (0.0 to 1.0) 216 */ 217 public double getNewPutErrorRate(long totalOperations) { 218 if (totalOperations == 0) { 219 return 0.0; 220 } 221 return (double) newPutErrorCount.get() / totalOperations; 222 } 223 224 /** 225 * 获取搜索差异率 226 * 227 * @param totalSearches total number of searches 228 * @return difference rate (0.0 to 1.0) 229 */ 230 public double getSearchDifferenceRate(long totalSearches) { 231 if (totalSearches == 0) { 232 return 0.0; 233 } 234 return (double) searchDiffCount.get() / totalSearches; 235 } 236}