{"record":{"id":"679502f5eb195388","repo":"lionsoul2014/ip2region","slug":"searcherpool-is-closing","errorCode":null,"errorMessage":"SearcherPool is closing","messagePattern":"SearcherPool is closing","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"warning","filePath":"binding/cangjie/src/service/searcher_pool.cj","lineNumber":49,"sourceCode":"    }\n\n    // borrow takes a Searcher from the pool, blocking until one is available.\n    // Throws if the pool is closing or closed.\n    public func borrow(): Searcher {\n        while (!this.closing.load()) {\n            if (this.semaphore.tryAcquire(amount: 1)) {\n                let opt = this.queue.remove()\n                if (opt.isNone()) {\n                    this.semaphore.release(amount: 1)\n                    continue\n                }\n                let s = opt.getOrThrow()\n                this.loanCnt.fetchAdd(1)\n                return s\n            }\n            sleep(Duration.millisecond * 5)\n        }\n        throw Exception(\"SearcherPool is closing\")\n    }\n\n    // return_ returns a Searcher to the pool, or closes it if the pool is shutting down.\n    public func return_(searcher: Searcher) {\n        if (this.closing.load()) {\n            searcher.close()\n        } else {\n            this.queue.add(searcher)\n            this.semaphore.release(amount: 1)\n        }\n        this.loanCnt.fetchSub(1)\n    }\n\n    // close closes the pool with a default 10 second timeout.\n    public func close() {\n        this.closeTimeout(Duration.second * 10)\n    }\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/lionsoul2014/ip2region/blob/c1a1fc7d5941760db3f8431dc05c48cf7f0e30a1/binding/cangjie/src/service/searcher_pool.cj#L31-L67","documentation":"SearcherPool.borrow waits (5 ms at a time) for a free Searcher, but if the pool is shutting down (closing flag set) it stops waiting and throws 'SearcherPool is closing'. This prevents lookups from starting during shutdown and from being stranded without ever being returned.","triggerScenarios":"Calling borrow() concurrently with close()/shutdown of the pool; a long shutdown overlapping in-flight request handling; closing the pool while a burst of borrows is still queued.","commonSituations":"Graceful server shutdown racing with late HTTP requests, health-check probes firing during process termination, test teardown issuing one more lookup after pool.close().","solutions":["Check the pool's closing state (or coordinate with a lifecycle flag) before borrowing","Stop accepting new work and drain in-flight requests before calling close on the pool","Catch this exception and fail the request gracefully (e.g. 503) during shutdown","Delay pool.close() until all workers that borrow have exited"],"exampleFix":"// before\nlet s = pool.borrow() // may throw during shutdown\n// after\nif (pool.isClosing()) { return ErrorResponse(503, \"shutting down\") }\nlet s = pool.borrow()","handlingStrategy":"try-catch","validationCode":"// Cangjie: skip borrow when shutting down\nif (pool.closing.load()) { return Response(503, \"shutting down\") }","typeGuard":null,"tryCatchPattern":"try {\n    let s = pool.borrow()\n    // ... lookup ...\n    pool.return_(s)\n} catch (e: Exception) {\n    if (e.message == \"SearcherPool is closing\") { return Response(503, \"shutting down\") }\n    throw e\n}","preventionTips":["Drain in-flight requests before closing the pool","Expose and check a shutting-down flag in request handlers","Return searchers promptly via return_ so borrows do not block near shutdown","In tests, close the pool only after all lookup goroutines/tasks finish"],"tags":["cangjie","concurrency","pool-shutdown","lifecycle"],"backgroundTag":"pool-shutdown-during-borrow","analyzedSha":"c1a1fc7d5941760db3f8431dc05c48cf7f0e30a1","analyzedAt":"2026-09-02T16:58:39.988Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T21:17:11.164Z"}