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}