001package gudusoft.gsqlparser; 002 003import java.util.concurrent.*; 004import java.util.concurrent.atomic.AtomicInteger; 005import java.util.Map; 006 007/** 008 * Thread-safe parser pool implementation to replace the singleton pattern. 009 * This pool dramatically improves performance in multi-threaded environments 010 * by eliminating synchronized method bottlenecks. 011 * 012 * Each database vendor has its own pool of parser instances that can be 013 * borrowed and returned for reuse. 014 */ 015public class TParserPool { 016 017 // Pool configuration 018 private static final int DEFAULT_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2; 019 private static final int MAX_POOL_SIZE = 64; 020 private static final long BORROW_TIMEOUT_MS = 5000; 021 022 // Pools for each database vendor 023 private final ConcurrentHashMap<EDbVendor, BlockingQueue<TGSqlParser>> parserPools; 024 private final ConcurrentHashMap<EDbVendor, AtomicInteger> poolSizes; 025 private final ConcurrentHashMap<EDbVendor, Semaphore> poolSemaphores; 026 027 // Pool statistics for monitoring 028 private final ConcurrentHashMap<EDbVendor, AtomicInteger> borrowCount; 029 private final ConcurrentHashMap<EDbVendor, AtomicInteger> returnCount; 030 private final ConcurrentHashMap<EDbVendor, AtomicInteger> createCount; 031 032 private final int poolSize; 033 private volatile boolean shutdown = false; 034 035 /** 036 * Creates a parser pool with default size 037 */ 038 public TParserPool() { 039 this(DEFAULT_POOL_SIZE); 040 } 041 042 /** 043 * Creates a parser pool with specified size 044 * @param poolSize Size of the pool for each vendor 045 */ 046 public TParserPool(int poolSize) { 047 this.poolSize = Math.min(poolSize, MAX_POOL_SIZE); 048 this.parserPools = new ConcurrentHashMap<>(); 049 this.poolSizes = new ConcurrentHashMap<>(); 050 this.poolSemaphores = new ConcurrentHashMap<>(); 051 this.borrowCount = new ConcurrentHashMap<>(); 052 this.returnCount = new ConcurrentHashMap<>(); 053 this.createCount = new ConcurrentHashMap<>(); 054 } 055 056 /** 057 * Borrows a parser from the pool for the specified vendor. 058 * If no parser is available, creates a new one up to the pool limit. 059 * 060 * @param vendor Database vendor 061 * @return Parser instance 062 * @throws InterruptedException if interrupted while waiting 063 */ 064 public TGSqlParser borrowParser(EDbVendor vendor) throws InterruptedException { 065 if (shutdown) { 066 throw new IllegalStateException("Parser pool has been shut down"); 067 } 068 069 // Get or create the semaphore for this vendor 070 Semaphore semaphore = poolSemaphores.computeIfAbsent(vendor, 071 v -> new Semaphore(poolSize, true)); 072 073 // Try to acquire a permit with timeout 074 if (!semaphore.tryAcquire(BORROW_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 075 throw new TimeoutException("Timeout waiting for available parser"); 076 } 077 078 try { 079 // Get or create the pool for this vendor 080 BlockingQueue<TGSqlParser> pool = parserPools.computeIfAbsent(vendor, 081 v -> new LinkedBlockingQueue<>()); 082 083 // Update borrow statistics 084 borrowCount.computeIfAbsent(vendor, v -> new AtomicInteger(0)).incrementAndGet(); 085 086 // Try to get an existing parser from the pool 087 TGSqlParser parser = pool.poll(); 088 089 if (parser == null) { 090 // No parser available, create a new one 091 parser = createParser(vendor); 092 AtomicInteger currentSize = poolSizes.computeIfAbsent(vendor, 093 v -> new AtomicInteger(0)); 094 currentSize.incrementAndGet(); 095 createCount.computeIfAbsent(vendor, v -> new AtomicInteger(0)).incrementAndGet(); 096 } 097 098 // Reset parser state before returning 099 resetParser(parser); 100 return parser; 101 102 } catch (Exception e) { 103 // Release permit if we fail to get a parser 104 semaphore.release(); 105 throw new RuntimeException("Failed to borrow parser", e); 106 } 107 } 108 109 /** 110 * Returns a parser to the pool for reuse. 111 * 112 * @param vendor Database vendor 113 * @param parser Parser instance to return 114 */ 115 public void returnParser(EDbVendor vendor, TGSqlParser parser) { 116 if (parser == null) { 117 return; 118 } 119 120 if (shutdown) { 121 // Don't return to pool if shutting down 122 return; 123 } 124 125 BlockingQueue<TGSqlParser> pool = parserPools.get(vendor); 126 if (pool != null) { 127 // Reset parser state before returning to pool 128 resetParser(parser); 129 130 // Try to return parser to pool 131 if (pool.offer(parser)) { 132 returnCount.computeIfAbsent(vendor, v -> new AtomicInteger(0)).incrementAndGet(); 133 } 134 } 135 136 // Release the semaphore permit 137 Semaphore semaphore = poolSemaphores.get(vendor); 138 if (semaphore != null) { 139 semaphore.release(); 140 } 141 } 142 143 /** 144 * Executes a function with a borrowed parser and automatically returns it. 145 * This is the recommended way to use the pool. 146 * 147 * @param vendor Database vendor 148 * @param function Function to execute with the parser 149 * @return Result of the function 150 */ 151 public <T> T withParser(EDbVendor vendor, ParserFunction<T> function) throws Exception { 152 TGSqlParser parser = null; 153 try { 154 parser = borrowParser(vendor); 155 return function.apply(parser); 156 } finally { 157 if (parser != null) { 158 returnParser(vendor, parser); 159 } 160 } 161 } 162 163 /** 164 * Creates a new parser instance for the specified vendor. 165 */ 166 private TGSqlParser createParser(EDbVendor vendor) { 167 return new TGSqlParser(vendor); 168 } 169 170 /** 171 * Resets parser state for reuse. 172 */ 173 private void resetParser(TGSqlParser parser) { 174 if (parser != null) { 175 // Clear previous SQL text and results 176 parser.sqltext = null; 177 parser.sqlfilename = null; 178 if (parser.sourcetokenlist != null) { 179 parser.sourcetokenlist.clear(); 180 } 181 if (parser.sqlstatements != null) { 182 parser.sqlstatements.clear(); 183 } 184 // Note: Some internal parser state cannot be reset from outside 185 // The parser will handle this internally on next parse 186 } 187 } 188 189 /** 190 * Gets pool statistics for monitoring. 191 */ 192 public PoolStatistics getStatistics(EDbVendor vendor) { 193 return new PoolStatistics( 194 poolSizes.getOrDefault(vendor, new AtomicInteger(0)).get(), 195 borrowCount.getOrDefault(vendor, new AtomicInteger(0)).get(), 196 returnCount.getOrDefault(vendor, new AtomicInteger(0)).get(), 197 createCount.getOrDefault(vendor, new AtomicInteger(0)).get(), 198 parserPools.containsKey(vendor) ? parserPools.get(vendor).size() : 0 199 ); 200 } 201 202 /** 203 * Pre-warms the pool by creating parsers in advance. 204 * This avoids the initialization cost during actual usage. 205 * 206 * @param vendor Database vendor 207 * @param count Number of parsers to pre-create (up to poolSize) 208 */ 209 public void prewarm(EDbVendor vendor, int count) { 210 if (shutdown) { 211 throw new IllegalStateException("Parser pool has been shut down"); 212 } 213 214 int toCreate = Math.min(count, poolSize); 215 BlockingQueue<TGSqlParser> pool = parserPools.computeIfAbsent(vendor, 216 v -> new LinkedBlockingQueue<>()); 217 218 for (int i = 0; i < toCreate; i++) { 219 TGSqlParser parser = createParser(vendor); 220 resetParser(parser); 221 pool.offer(parser); 222 poolSizes.computeIfAbsent(vendor, v -> new AtomicInteger(0)).incrementAndGet(); 223 createCount.computeIfAbsent(vendor, v -> new AtomicInteger(0)).incrementAndGet(); 224 } 225 } 226 227 /** 228 * Shuts down the pool and clears all parsers. 229 */ 230 public void shutdown() { 231 shutdown = true; 232 parserPools.clear(); 233 poolSizes.clear(); 234 poolSemaphores.clear(); 235 } 236 237 /** 238 * Functional interface for parser operations. 239 */ 240 @FunctionalInterface 241 public interface ParserFunction<T> { 242 T apply(TGSqlParser parser) throws Exception; 243 } 244 245 /** 246 * Pool statistics for monitoring. 247 */ 248 public static class PoolStatistics { 249 public final int totalParsers; 250 public final int borrowCount; 251 public final int returnCount; 252 public final int createCount; 253 public final int availableParsers; 254 255 public PoolStatistics(int totalParsers, int borrowCount, int returnCount, 256 int createCount, int availableParsers) { 257 this.totalParsers = totalParsers; 258 this.borrowCount = borrowCount; 259 this.returnCount = returnCount; 260 this.createCount = createCount; 261 this.availableParsers = availableParsers; 262 } 263 264 @Override 265 public String toString() { 266 return String.format("PoolStats[total=%d, available=%d, borrows=%d, returns=%d, creates=%d]", 267 totalParsers, availableParsers, borrowCount, returnCount, createCount); 268 } 269 } 270 271 /** 272 * Custom exception for timeout scenarios. 273 */ 274 public static class TimeoutException extends RuntimeException { 275 public TimeoutException(String message) { 276 super(message); 277 } 278 } 279}