1
+ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
+
3
+ /* Fluent Bit
4
+ * ==========
5
+ * Copyright (C) 2015-2025 The Fluent Bit Authors
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ #include <fluent-bit/flb_info.h>
21
+ #include <fluent-bit/flb_mem.h>
22
+ #include <fluent-bit/flb_time.h>
23
+
24
+ #include "flb_tests_internal.h"
25
+
26
+ /* Import the sampling functions from the processor plugin */
27
+ #include "../../plugins/processor_log_sampling/log_sampling.h"
28
+
29
+ static void test_fixed_window_basic ()
30
+ {
31
+ struct sampling_state state = {0 };
32
+ int result ;
33
+ int i ;
34
+ int sampled_count = 0 ;
35
+ time_t current_time = 1000 ; /* Start time */
36
+ int window_size = 60 ;
37
+ int max_logs = 5 ;
38
+
39
+ /* Initialize state */
40
+ state .window_start = current_time ;
41
+ state .current_window_count = 0 ;
42
+
43
+ /* Test: First 5 logs should be sampled */
44
+ for (i = 0 ; i < 10 ; i ++ ) {
45
+ result = flb_sampling_fixed_window (& state , current_time , window_size , max_logs );
46
+ if (result == FLB_TRUE ) {
47
+ sampled_count ++ ;
48
+ }
49
+ }
50
+
51
+ TEST_CHECK (sampled_count == 5 );
52
+ TEST_MSG ("Fixed window: sampled %d out of 10 (expected 5)" , sampled_count );
53
+
54
+ /* Test: New window should reset the count */
55
+ current_time += window_size + 1 ; /* Move to next window */
56
+ sampled_count = 0 ;
57
+
58
+ for (i = 0 ; i < 3 ; i ++ ) {
59
+ result = flb_sampling_fixed_window (& state , current_time , window_size , max_logs );
60
+ if (result == FLB_TRUE ) {
61
+ sampled_count ++ ;
62
+ }
63
+ }
64
+
65
+ TEST_CHECK (sampled_count == 3 );
66
+ TEST_MSG ("Fixed window (new): sampled %d out of 3 (expected 3)" , sampled_count );
67
+ }
68
+
69
+ static void test_sliding_window_basic ()
70
+ {
71
+ struct sampling_state state = {0 };
72
+ int result ;
73
+ int i ;
74
+ int sampled_count = 0 ;
75
+ time_t current_time = 1000 ;
76
+ int window_size = 10 ;
77
+ int max_logs = 5 ;
78
+
79
+ /* Initialize sliding window buckets */
80
+ state .bucket_count = window_size ;
81
+ state .buckets = flb_calloc (state .bucket_count , sizeof (* state .buckets ));
82
+ TEST_CHECK (state .buckets != NULL );
83
+
84
+ /* Test: First 5 logs should be sampled */
85
+ for (i = 0 ; i < 7 ; i ++ ) {
86
+ result = flb_sampling_sliding_window (& state , current_time , window_size , max_logs );
87
+ if (result == FLB_TRUE ) {
88
+ sampled_count ++ ;
89
+ }
90
+ /* Advance time slightly to distribute across buckets */
91
+ if (i % 2 == 0 ) {
92
+ current_time ++ ;
93
+ }
94
+ }
95
+
96
+ TEST_CHECK (sampled_count == 5 );
97
+ TEST_MSG ("Sliding window: sampled %d out of 7 (expected 5)" , sampled_count );
98
+
99
+ /* Test: Old entries should expire */
100
+ current_time += window_size + 2 ; /* Move past window */
101
+ sampled_count = 0 ;
102
+
103
+ for (i = 0 ; i < 3 ; i ++ ) {
104
+ result = flb_sampling_sliding_window (& state , current_time , window_size , max_logs );
105
+ if (result == FLB_TRUE ) {
106
+ sampled_count ++ ;
107
+ }
108
+ }
109
+
110
+ TEST_CHECK (sampled_count == 3 );
111
+ TEST_MSG ("Sliding window (after expiry): sampled %d out of 3 (expected 3)" , sampled_count );
112
+
113
+ flb_free (state .buckets );
114
+ }
115
+
116
+ static void test_exponential_decay_basic ()
117
+ {
118
+ int result ;
119
+ int i ;
120
+ int sampled_count ;
121
+ time_t window_start = 1000 ;
122
+ time_t current_time = 1000 ;
123
+ double base_rate = 1.0 ; /* 100% initially */
124
+ double decay_factor = 0.5 ; /* 50% reduction per interval */
125
+ int decay_interval = 10 ;
126
+
127
+ /* Seed random for reproducible tests */
128
+ srand (12345 );
129
+
130
+ /* Test: First interval should sample most logs (rate=1.0) */
131
+ sampled_count = 0 ;
132
+ for (i = 0 ; i < 100 ; i ++ ) {
133
+ result = flb_sampling_exponential (window_start , current_time ,
134
+ base_rate , decay_factor , decay_interval );
135
+ if (result == FLB_TRUE ) {
136
+ sampled_count ++ ;
137
+ }
138
+ }
139
+
140
+ /* With rate=1.0, should sample all or nearly all */
141
+ TEST_CHECK (sampled_count >= 95 );
142
+ TEST_MSG ("Exponential (interval 0): sampled %d out of 100 (expected >= 95)" , sampled_count );
143
+
144
+ /* Test: Second interval should sample about 50% */
145
+ current_time = window_start + decay_interval ;
146
+ sampled_count = 0 ;
147
+
148
+ for (i = 0 ; i < 100 ; i ++ ) {
149
+ result = flb_sampling_exponential (window_start , current_time ,
150
+ base_rate , decay_factor , decay_interval );
151
+ if (result == FLB_TRUE ) {
152
+ sampled_count ++ ;
153
+ }
154
+ }
155
+
156
+ /* With rate=0.5, should sample roughly 40-60% */
157
+ TEST_CHECK (sampled_count >= 40 && sampled_count <= 60 );
158
+ TEST_MSG ("Exponential (interval 1): sampled %d out of 100 (expected 40-60)" , sampled_count );
159
+
160
+ /* Test: Third interval should sample about 25% */
161
+ current_time = window_start + (2 * decay_interval );
162
+ sampled_count = 0 ;
163
+
164
+ for (i = 0 ; i < 100 ; i ++ ) {
165
+ result = flb_sampling_exponential (window_start , current_time ,
166
+ base_rate , decay_factor , decay_interval );
167
+ if (result == FLB_TRUE ) {
168
+ sampled_count ++ ;
169
+ }
170
+ }
171
+
172
+ /* With rate=0.25, should sample roughly 20-30% */
173
+ TEST_CHECK (sampled_count >= 15 && sampled_count <= 35 );
174
+ TEST_MSG ("Exponential (interval 2): sampled %d out of 100 (expected 15-35)" , sampled_count );
175
+ }
176
+
177
+ static void test_fixed_window_edge_cases ()
178
+ {
179
+ struct sampling_state state = {0 };
180
+ int result ;
181
+ time_t current_time = 1000 ;
182
+ int window_size = 60 ;
183
+ int max_logs = 0 ; /* Edge case: no logs allowed */
184
+
185
+ /* Test: max_logs = 0 should reject all */
186
+ state .window_start = current_time ;
187
+ state .current_window_count = 0 ;
188
+
189
+ result = flb_sampling_fixed_window (& state , current_time , window_size , max_logs );
190
+ TEST_CHECK (result == FLB_FALSE );
191
+
192
+ /* Test: Window boundary */
193
+ max_logs = 1 ;
194
+ state .current_window_count = 0 ;
195
+
196
+ result = flb_sampling_fixed_window (& state , current_time , window_size , max_logs );
197
+ TEST_CHECK (result == FLB_TRUE );
198
+
199
+ /* Move to exact window boundary */
200
+ current_time = state .window_start + window_size ;
201
+ result = flb_sampling_fixed_window (& state , current_time , window_size , max_logs );
202
+ TEST_CHECK (result == FLB_TRUE ); /* New window should allow */
203
+ TEST_CHECK (state .current_window_count == 1 );
204
+ }
205
+
206
+ static void test_sliding_window_edge_cases ()
207
+ {
208
+ struct sampling_state state = {0 };
209
+ int result ;
210
+ time_t current_time = 1000 ;
211
+ int window_size = 1 ; /* Minimal window */
212
+ int max_logs = 1 ;
213
+
214
+ /* Initialize with minimal bucket */
215
+ state .bucket_count = 1 ;
216
+ state .buckets = flb_calloc (1 , sizeof (* state .buckets ));
217
+ TEST_CHECK (state .buckets != NULL );
218
+
219
+ /* Test: Single bucket behavior */
220
+ result = flb_sampling_sliding_window (& state , current_time , window_size , max_logs );
221
+ TEST_CHECK (result == FLB_TRUE );
222
+
223
+ result = flb_sampling_sliding_window (& state , current_time , window_size , max_logs );
224
+ TEST_CHECK (result == FLB_FALSE ); /* Already at limit */
225
+
226
+ /* Test: Bucket expiry */
227
+ current_time += window_size + 1 ;
228
+ result = flb_sampling_sliding_window (& state , current_time , window_size , max_logs );
229
+ TEST_CHECK (result == FLB_TRUE ); /* Old bucket should be expired */
230
+
231
+ flb_free (state .buckets );
232
+ }
233
+
234
+ TEST_LIST = {
235
+ { "fixed_window_basic" , test_fixed_window_basic },
236
+ { "sliding_window_basic" , test_sliding_window_basic },
237
+ { "exponential_decay_basic" , test_exponential_decay_basic },
238
+ { "fixed_window_edge_cases" , test_fixed_window_edge_cases },
239
+ { "sliding_window_edge_cases" , test_sliding_window_edge_cases },
240
+ { 0 }
241
+ };
0 commit comments