| 145 | |
| 146 | impl Drop for QueryLeaseScope { |
| 147 | fn drop(&mut self) { |
| 148 | if self.descriptor_ids.is_empty() { |
| 149 | return; |
| 150 | } |
| 151 | let Some(shared) = self.shared.upgrade() else { |
| 152 | return; |
| 153 | }; |
| 154 | // Decrement each refcount and collect the ids whose |
| 155 | // count just hit zero — those need the actual raft |
| 156 | // release. |
| 157 | let mut to_release = Vec::new(); |
| 158 | for id in self.descriptor_ids.drain(..) { |
| 159 | let new_count = shared.lease_refcount.decrement(&id); |
| 160 | if new_count == 0 { |
| 161 | to_release.push(id); |
| 162 | } |
| 163 | } |
| 164 | if to_release.is_empty() { |
| 165 | return; |
| 166 | } |
| 167 | // Release is sync + uses `block_in_place`; spawning to |
| 168 | // the tokio runtime lets `Drop` return immediately |
| 169 | // while the release propose proceeds in the background. |
| 170 | // This is best-effort: if the runtime is already shut |
| 171 | // down (e.g., test teardown race) the spawn fails |
| 172 | // silently and the lease drains via TTL. |
| 173 | if let Ok(handle) = tokio::runtime::Handle::try_current() { |
| 174 | let shared = Arc::clone(&shared); |
| 175 | handle.spawn(async move { |
| 176 | let shared_inner = Arc::clone(&shared); |
| 177 | let descriptor_ids = to_release.clone(); |
| 178 | let result = tokio::task::spawn_blocking(move || { |
| 179 | shared_inner.release_descriptor_leases(descriptor_ids) |
| 180 | }) |
| 181 | .await; |
| 182 | match result { |
| 183 | Ok(Ok(())) => {} |
| 184 | Ok(Err(e)) => { |
| 185 | warn!( |
| 186 | error = %e, |
| 187 | count = to_release.len(), |
| 188 | "QueryLeaseScope drop: background release failed" |
| 189 | ); |
| 190 | } |
| 191 | Err(e) => { |
| 192 | warn!( |
| 193 | error = %e, |
| 194 | count = to_release.len(), |
| 195 | "QueryLeaseScope drop: spawn_blocking panicked" |
| 196 | ); |
| 197 | } |
| 198 | } |
| 199 | }); |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | #[cfg(test)] |