Skip to main content

reifydb_core/value/column/pool/
guard.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4//! Automatic pool management through RAII guards
5//!
6//! This module provides the `PooledGuard` type that automatically returns
7//! containers to their respective pools when dropped, implementing the RAII
8//! pattern for memory pool management.
9
10use std::{
11	fmt::{Debug, Formatter},
12	ops::{Deref, DerefMut},
13	rc::{Rc, Weak},
14};
15
16use reifydb_type::value::{
17	container::{
18		blob::BlobContainer, bool::BoolContainer, number::NumberContainer, row::RowNumberContainer,
19		temporal::TemporalContainer, utf8::Utf8Container, uuid::UuidContainer, *,
20	},
21	date::Date,
22	datetime::DateTime,
23	duration::Duration,
24	time::Time,
25	uuid::{Uuid4, Uuid7},
26};
27
28use super::{Pools, PoolsInner, allocator::PoolAllocator};
29
30/// Trait for containers that can be released back to a pool
31pub trait Releasable: Clone + Debug {
32	fn release_to_pool(self, pools: &Pools);
33}
34
35// Implement Releasable for all container types
36impl Releasable for BoolContainer {
37	fn release_to_pool(self, pools: &Pools) {
38		pools.bool_pool().release(self);
39	}
40}
41
42impl Releasable for Utf8Container {
43	fn release_to_pool(self, pools: &Pools) {
44		pools.string_pool().release(self);
45	}
46}
47
48impl Releasable for BlobContainer {
49	fn release_to_pool(self, pools: &Pools) {
50		pools.blob_pool().release(self);
51	}
52}
53
54impl Releasable for RowNumberContainer {
55	fn release_to_pool(self, pools: &Pools) {
56		pools.row_number_pool().release(self);
57	}
58}
59
60impl Releasable for NumberContainer<i32> {
61	fn release_to_pool(self, pools: &Pools) {
62		pools.i32_pool().release(self);
63	}
64}
65
66impl Releasable for NumberContainer<i64> {
67	fn release_to_pool(self, pools: &Pools) {
68		pools.i64_pool().release(self);
69	}
70}
71
72impl Releasable for NumberContainer<f32> {
73	fn release_to_pool(self, pools: &Pools) {
74		pools.f32_pool().release(self);
75	}
76}
77
78impl Releasable for NumberContainer<f64> {
79	fn release_to_pool(self, pools: &Pools) {
80		pools.f64_pool().release(self);
81	}
82}
83
84impl Releasable for NumberContainer<i8> {
85	fn release_to_pool(self, pools: &Pools) {
86		pools.i8_pool().release(self);
87	}
88}
89
90impl Releasable for NumberContainer<i16> {
91	fn release_to_pool(self, pools: &Pools) {
92		pools.i16_pool().release(self);
93	}
94}
95
96impl Releasable for NumberContainer<i128> {
97	fn release_to_pool(self, pools: &Pools) {
98		pools.i128_pool().release(self);
99	}
100}
101
102impl Releasable for NumberContainer<u8> {
103	fn release_to_pool(self, pools: &Pools) {
104		pools.u8_pool().release(self);
105	}
106}
107
108impl Releasable for NumberContainer<u16> {
109	fn release_to_pool(self, pools: &Pools) {
110		pools.u16_pool().release(self);
111	}
112}
113
114impl Releasable for NumberContainer<u32> {
115	fn release_to_pool(self, pools: &Pools) {
116		pools.u32_pool().release(self);
117	}
118}
119
120impl Releasable for NumberContainer<u64> {
121	fn release_to_pool(self, pools: &Pools) {
122		pools.u64_pool().release(self);
123	}
124}
125
126impl Releasable for NumberContainer<u128> {
127	fn release_to_pool(self, pools: &Pools) {
128		pools.u128_pool().release(self);
129	}
130}
131
132impl Releasable for TemporalContainer<Date> {
133	fn release_to_pool(self, pools: &Pools) {
134		pools.date_pool().release(self);
135	}
136}
137
138impl Releasable for TemporalContainer<DateTime> {
139	fn release_to_pool(self, pools: &Pools) {
140		pools.datetime_pool().release(self);
141	}
142}
143
144impl Releasable for TemporalContainer<Time> {
145	fn release_to_pool(self, pools: &Pools) {
146		pools.time_pool().release(self);
147	}
148}
149
150impl Releasable for TemporalContainer<Duration> {
151	fn release_to_pool(self, pools: &Pools) {
152		pools.duration_pool().release(self);
153	}
154}
155
156impl Releasable for UuidContainer<Uuid4> {
157	fn release_to_pool(self, pools: &Pools) {
158		pools.uuid4_pool().release(self);
159	}
160}
161
162impl Releasable for UuidContainer<Uuid7> {
163	fn release_to_pool(self, pools: &Pools) {
164		pools.uuid7_pool().release(self);
165	}
166}
167
168/// A guard that automatically returns a container to its pool when dropped
169///
170/// This implements the RAII pattern for container pooling - when the guard goes
171/// out of scope, it automatically releases the contained value back to the pool
172/// for reuse.
173pub struct PooledGuard<T: Releasable> {
174	container: Option<T>,
175	pools: Weak<PoolsInner>,
176}
177
178impl<T: Releasable> Debug for PooledGuard<T> {
179	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180		Debug::fmt(&self.container, f)
181	}
182}
183
184impl<T: Releasable> PooledGuard<T> {
185	/// Create a new pooled guard with the given container and pool
186	/// reference
187	fn new(container: T, pools: Pools) -> Self {
188		Self {
189			container: Some(container),
190			pools: Rc::downgrade(&pools.0),
191		}
192	}
193
194	/// Clone the container and release the guard back to the pool
195	///
196	/// This returns a clone of the container while automatically releasing
197	/// the original back to the pool for reuse.
198	pub fn to_owned(mut self) -> T {
199		let container = self.container.take().expect("Container already taken");
200		let cloned_container = container.clone();
201
202		// Release the original container back to the pool
203		if let Some(pools_inner) = self.pools.upgrade() {
204			container.release_to_pool(&Pools(pools_inner));
205		}
206
207		cloned_container
208	}
209
210	/// Check if the container is still held by this guard
211	pub fn is_empty(&self) -> bool {
212		self.container.is_none()
213	}
214}
215
216// Container-specific constructors
217impl PooledGuard<BoolContainer> {
218	/// Create a new pooled BoolContainer with the specified capacity
219	pub fn new_bool(pools: Pools, capacity: usize) -> Self {
220		let container = pools.bool_pool().acquire(capacity);
221		Self::new(container, pools)
222	}
223}
224
225impl PooledGuard<Utf8Container> {
226	/// Create a new pooled StringContainer with the specified capacity
227	pub fn new_string(pools: Pools, capacity: usize) -> Self {
228		let container = pools.string_pool().acquire(capacity);
229		Self::new(container, pools)
230	}
231}
232
233impl PooledGuard<BlobContainer> {
234	/// Create a new pooled BlobContainer with the specified capacity
235	pub fn new_blob(pools: Pools, capacity: usize) -> Self {
236		let container = pools.blob_pool().acquire(capacity);
237		Self::new(container, pools)
238	}
239}
240
241impl PooledGuard<RowNumberContainer> {
242	/// Create a new pooled RowNumberContainer with the specified capacity
243	pub fn new_row_number(pools: Pools, capacity: usize) -> Self {
244		let container = pools.row_number_pool().acquire(capacity);
245		Self::new(container, pools)
246	}
247}
248
249// Numeric container constructors
250impl PooledGuard<NumberContainer<i32>> {
251	/// Create a new pooled NumberContainer<i32> with the specified capacity
252	pub fn new_i32(pools: Pools, capacity: usize) -> Self {
253		let container = pools.i32_pool().acquire(capacity);
254		Self::new(container, pools)
255	}
256}
257
258impl PooledGuard<NumberContainer<i64>> {
259	/// Create a new pooled NumberContainer<i64> with the specified capacity
260	pub fn new_i64(pools: Pools, capacity: usize) -> Self {
261		let container = pools.i64_pool().acquire(capacity);
262		Self::new(container, pools)
263	}
264}
265
266impl PooledGuard<NumberContainer<f32>> {
267	/// Create a new pooled NumberContainer<f32> with the specified capacity
268	pub fn new_f32(pools: Pools, capacity: usize) -> Self {
269		let container = pools.f32_pool().acquire(capacity);
270		Self::new(container, pools)
271	}
272}
273
274impl PooledGuard<NumberContainer<f64>> {
275	/// Create a new pooled NumberContainer<f64> with the specified capacity
276	pub fn new_f64(pools: Pools, capacity: usize) -> Self {
277		let container = pools.f64_pool().acquire(capacity);
278		Self::new(container, pools)
279	}
280}
281
282impl PooledGuard<NumberContainer<i8>> {
283	/// Create a new pooled NumberContainer<i8> with the specified capacity
284	pub fn new_i8(pools: Pools, capacity: usize) -> Self {
285		let container = pools.i8_pool().acquire(capacity);
286		Self::new(container, pools)
287	}
288}
289
290impl PooledGuard<NumberContainer<i16>> {
291	/// Create a new pooled NumberContainer<i16> with the specified capacity
292	pub fn new_i16(pools: Pools, capacity: usize) -> Self {
293		let container = pools.i16_pool().acquire(capacity);
294		Self::new(container, pools)
295	}
296}
297
298impl PooledGuard<NumberContainer<i128>> {
299	/// Create a new pooled NumberContainer<i128> with the specified
300	/// capacity
301	pub fn new_i128(pools: Pools, capacity: usize) -> Self {
302		let container = pools.i128_pool().acquire(capacity);
303		Self::new(container, pools)
304	}
305}
306
307impl PooledGuard<NumberContainer<u8>> {
308	/// Create a new pooled NumberContainer<u8> with the specified capacity
309	pub fn new_u8(pools: Pools, capacity: usize) -> Self {
310		let container = pools.u8_pool().acquire(capacity);
311		Self::new(container, pools)
312	}
313}
314
315impl PooledGuard<NumberContainer<u16>> {
316	/// Create a new pooled NumberContainer<u16> with the specified capacity
317	pub fn new_u16(pools: Pools, capacity: usize) -> Self {
318		let container = pools.u16_pool().acquire(capacity);
319		Self::new(container, pools)
320	}
321}
322
323impl PooledGuard<NumberContainer<u32>> {
324	/// Create a new pooled NumberContainer<u32> with the specified capacity
325	pub fn new_u32(pools: Pools, capacity: usize) -> Self {
326		let container = pools.u32_pool().acquire(capacity);
327		Self::new(container, pools)
328	}
329}
330
331impl PooledGuard<NumberContainer<u64>> {
332	/// Create a new pooled NumberContainer<u64> with the specified capacity
333	pub fn new_u64(pools: Pools, capacity: usize) -> Self {
334		let container = pools.u64_pool().acquire(capacity);
335		Self::new(container, pools)
336	}
337}
338
339impl PooledGuard<NumberContainer<u128>> {
340	/// Create a new pooled NumberContainer<u128> with the specified
341	/// capacity
342	pub fn new_u128(pools: Pools, capacity: usize) -> Self {
343		let container = pools.u128_pool().acquire(capacity);
344		Self::new(container, pools)
345	}
346}
347
348// Temporal container constructors
349impl PooledGuard<TemporalContainer<Date>> {
350	/// Create a new pooled TemporalContainer<Date> with the specified
351	/// capacity
352	pub fn new_date(pools: Pools, capacity: usize) -> Self {
353		let container = pools.date_pool().acquire(capacity);
354		Self::new(container, pools)
355	}
356}
357
358impl PooledGuard<TemporalContainer<DateTime>> {
359	/// Create a new pooled TemporalContainer<DateTime> with the specified
360	/// capacity
361	pub fn new_datetime(pools: Pools, capacity: usize) -> Self {
362		let container = pools.datetime_pool().acquire(capacity);
363		Self::new(container, pools)
364	}
365}
366
367impl PooledGuard<TemporalContainer<Time>> {
368	/// Create a new pooled TemporalContainer<Time> with the specified
369	/// capacity
370	pub fn new_time(pools: Pools, capacity: usize) -> Self {
371		let container = pools.time_pool().acquire(capacity);
372		Self::new(container, pools)
373	}
374}
375
376impl PooledGuard<TemporalContainer<Duration>> {
377	/// Create a new pooled TemporalContainer<Duration> with the specified
378	/// capacity
379	pub fn new_duration(pools: Pools, capacity: usize) -> Self {
380		let container = pools.duration_pool().acquire(capacity);
381		Self::new(container, pools)
382	}
383}
384
385// UUID container constructors
386impl PooledGuard<UuidContainer<Uuid4>> {
387	/// Create a new pooled UuidContainer<Uuid4> with the specified capacity
388	pub fn new_uuid4(pools: Pools, capacity: usize) -> Self {
389		let container = pools.uuid4_pool().acquire(capacity);
390		Self::new(container, pools)
391	}
392}
393
394impl PooledGuard<UuidContainer<Uuid7>> {
395	/// Create a new pooled UuidContainer<Uuid7> with the specified capacity
396	pub fn new_uuid7(pools: Pools, capacity: usize) -> Self {
397		let container = pools.uuid7_pool().acquire(capacity);
398		Self::new(container, pools)
399	}
400}
401
402// Implement Drop for automatic pool release
403impl<T: Releasable> Drop for PooledGuard<T> {
404	fn drop(&mut self) {
405		if let (Some(container), Some(pools_inner)) = (self.container.take(), self.pools.upgrade()) {
406			container.release_to_pool(&Pools(pools_inner));
407		}
408	}
409}
410
411// Implement Deref for transparent access to the container
412impl<T: Releasable> Deref for PooledGuard<T> {
413	type Target = T;
414
415	fn deref(&self) -> &Self::Target {
416		self.container.as_ref().expect("Container has been taken")
417	}
418}
419
420// Implement DerefMut for mutable access to the container
421impl<T: Releasable> DerefMut for PooledGuard<T> {
422	fn deref_mut(&mut self) -> &mut Self::Target {
423		self.container.as_mut().expect("Container has been taken")
424	}
425}
426
427#[cfg(test)]
428pub mod tests {
429	use super::*;
430
431	#[test]
432	fn test_bool_container_guard() {
433		let pools = Pools::default();
434
435		// Initial pool should be empty
436		let initial_stats = pools.bool_pool().stats();
437		assert_eq!(initial_stats.available, 0);
438
439		{
440			let guard = PooledGuard::new_bool(pools.clone(), 10);
441			assert!(guard.capacity() >= 10);
442			assert_eq!(guard.len(), 0);
443
444			// Pool should show one acquired
445			let stats = pools.bool_pool().stats();
446			assert_eq!(stats.total_acquired, 1);
447			assert_eq!(stats.available, 0);
448		} // Guard dropped here
449
450		// After drop, container should be returned to pool
451		let final_stats = pools.bool_pool().stats();
452		assert_eq!(final_stats.total_acquired, 1);
453		assert_eq!(final_stats.total_released, 1);
454		assert_eq!(final_stats.available, 1);
455	}
456
457	#[test]
458	fn test_number_container_guard() {
459		let pools = Pools::default();
460
461		{
462			let mut guard = PooledGuard::new_i32(pools.clone(), 20);
463			assert!(guard.capacity() >= 20);
464
465			// Test mutable access
466			guard.push(42);
467			guard.push(100);
468			assert_eq!(guard.len(), 2);
469			assert_eq!(guard.get(0), Some(&42));
470			assert_eq!(guard.get(1), Some(&100));
471		}
472
473		// Container should be returned to pool
474		let stats = pools.i32_pool().stats();
475		assert_eq!(stats.available, 1);
476	}
477
478	#[test]
479	fn test_string_container_guard() {
480		let pools = Pools::default();
481
482		{
483			let mut guard = PooledGuard::new_string(pools.clone(), 5);
484			guard.push("hello".to_string());
485			guard.push("world".to_string());
486			assert_eq!(guard.len(), 2);
487		}
488
489		let stats = pools.string_pool().stats();
490		assert_eq!(stats.available, 1);
491	}
492
493	#[test]
494	fn test_guard_to_owned() {
495		let pools = Pools::default();
496
497		let guard = PooledGuard::new_bool(pools.clone(), 10);
498		let container = guard.to_owned(); // Clone container and release guard to pool
499
500		assert!(container.capacity() >= 10);
501
502		// Pool should have received the original container back
503		let stats = pools.bool_pool().stats();
504		assert_eq!(stats.available, 1);
505		assert_eq!(stats.total_released, 1);
506
507		// We now have a cloned container that's independent of the pool
508		assert!(container.capacity() >= 10);
509	}
510
511	#[test]
512	fn test_multiple_guards_same_pool() {
513		let pools = Pools::default();
514
515		{
516			let _guard1 = PooledGuard::new_f32(pools.clone(), 100);
517			let _guard2 = PooledGuard::new_f32(pools.clone(), 200);
518			let _guard3 = PooledGuard::new_f32(pools.clone(), 50);
519
520			let stats = pools.f32_pool().stats();
521			assert_eq!(stats.total_acquired, 3);
522			assert_eq!(stats.available, 0);
523		}
524
525		// All should be returned
526		let final_stats = pools.f32_pool().stats();
527		assert_eq!(final_stats.total_released, 3);
528		assert_eq!(final_stats.available, 3);
529	}
530
531	#[test]
532	fn test_guard_reuse() {
533		let pools = Pools::default();
534
535		// First usage
536		{
537			let mut guard = PooledGuard::new_i64(pools.clone(), 50);
538			guard.push(123);
539			assert_eq!(guard.len(), 1);
540		}
541
542		// Second usage should reuse the same container
543		{
544			let guard = PooledGuard::new_i64(pools.clone(), 50);
545			// Container should be cleared from pool
546			assert_eq!(guard.len(), 0);
547			assert!(guard.capacity() >= 50);
548		}
549
550		let stats = pools.i64_pool().stats();
551		assert_eq!(stats.total_acquired, 2);
552		assert_eq!(stats.total_released, 2);
553		assert_eq!(stats.available, 1);
554	}
555
556	#[test]
557	fn test_temporal_containers() {
558		let pools = Pools::default();
559
560		{
561			let _date_guard = PooledGuard::new_date(pools.clone(), 10);
562			let _datetime_guard = PooledGuard::new_datetime(pools.clone(), 20);
563			let _time_guard = PooledGuard::new_time(pools.clone(), 30);
564			let _duration_guard = PooledGuard::new_duration(pools.clone(), 40);
565		}
566
567		let all_stats = pools.all_stats();
568		assert_eq!(all_stats["date"].available, 1);
569		assert_eq!(all_stats["datetime"].available, 1);
570		assert_eq!(all_stats["time"].available, 1);
571		assert_eq!(all_stats["duration"].available, 1);
572	}
573
574	#[test]
575	fn test_uuid_containers() {
576		let pools = Pools::default();
577
578		{
579			let _uuid4_guard = PooledGuard::new_uuid4(pools.clone(), 15);
580			let _uuid7_guard = PooledGuard::new_uuid7(pools.clone(), 25);
581		}
582
583		let all_stats = pools.all_stats();
584		assert_eq!(all_stats["uuid4"].available, 1);
585		assert_eq!(all_stats["uuid7"].available, 1);
586	}
587}