reifydb_core/value/column/pool/
guard.rs

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