{"record":{"id":"92735b88ce08858b","repo":"quickwit-oss/quickwit","slug":"closure-always-returns-some","errorCode":null,"errorMessage":"closure always returns Some","messagePattern":"closure always returns Some","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"quickwit/quickwit-search/src/search_permit_provider.rs","lineNumber":496,"sourceCode":"    fn send_if_still_running(&self, msg: SearchPermitMessage) {\n        if let Some(sender) = self.msg_sender.upgrade() {\n            sender\n                .send(msg)\n                // Receiver instance in the event loop is never dropped or\n                // closed as long as there is a strong sender reference.\n                .expect(\"Receiver should live longer than sender\");\n        }\n    }\n}\n\nimpl Drop for SearchPermit {\n    fn drop(&mut self) {\n        let prev = self\n            .total_job_cost\n            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {\n                Some(v.saturating_sub(self.job_cost))\n            })\n            .expect(\"closure always returns Some\");\n        if self.job_cost > prev {\n            warn!(\n                job_cost = self.job_cost,\n                total_job_cost = prev,\n                \"job cost underflow: more job cost released than allocated\"\n            );\n        }\n        // fetch_update returns the previous value, so subtract job_cost to get the new total.\n        SEARCHER_NODE_LOAD.set(prev.saturating_sub(self.job_cost) as f64);\n        self.send_if_still_running(SearchPermitMessage::Drop {\n            memory_size: self.memory_allocation,\n            warmup_slot_freed: self.warmup_slot_freed,\n        });\n    }\n}\n\npub struct SearchPermitFuture(oneshot::Receiver<SearchPermit>);\n","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-search/src/search_permit_provider.rs#L478-L514","documentation":"SearchPermit::drop decrements an AtomicU64 total_job_cost using fetch_update with a saturating_sub closure; since that closure always yields Some, the expect can only fire if the CAS failed 32+ times under extreme contention. Separately, the drop warns when more job cost is released than was allocated (underflow detected by comparing job_cost > prev).","triggerScenarios":"Dropping SearchPermits concurrently from many threads causing repeated CAS failure (theoretical), or a permit carrying a job_cost larger than the recorded total (real underflow, logged as warning).","commonSituations":"Buggy permit cloning/ownership allowing the same job_cost to be registered twice; permits dropped after total_job_cost was reset.","solutions":["Check that each permit's job_cost is added exactly once to total_job_cost","Look for the 'job cost underflow' warning to identify the unpaired allocation","If the expect itself fires, inspect CAS retry loops / very high concurrency and consider fetch_update retry logging"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before dropping a permit, sanity check:\nassert!(permit.job_cost <= total_job_cost.load(Ordering::Relaxed), \"permit cost exceeds tracked total\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never clone or duplicate SearchPermit; each cost must be added once","Treat the 'job cost underflow' warning as a bug signal, not noise","Prefer plain subtraction + debug_assert over fetch_update when contention is low"],"tags":["rust","atomic","concurrency","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}