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}