@cryptotaxi247 / netdata-1 / commits / a26cabbb5

Improve check for DBENGINE max datafile size allowed (#21390)

Enhance datafile size check to prevent overflow during writes

Stelios Fragkakis committed Dec 3, 2025 at 19:21 UTC a26cabbb5c49cec326b140c4f52c353f87fb4924
1 file changed +17 -16
src/database/engine/rrdengine.c
+17 -16
@@ -740,7 +740,7 @@ extent_flush_to_open(struct rrdengine_instance *ctx, struct extent_io_descriptor
740
741 // Main event loop callback
742
743 -static bool datafile_is_full(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile) {
743 +static bool datafile_is_full(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, uint64_t extent_size) {
744 bool ret = false;
745
746 spinlock_lock(&datafile->writers.spinlock);
@@ -754,7 +754,8 @@ static bool datafile_is_full(struct rrdengine_instance *ctx, struct rrdengine_da
754 }
755 #endif
756
757 - if(datafile->pos > rrdeng_target_data_file_size(ctx))
757 + // Check if adding this extent would exceed the target size
758 + if(datafile->pos + extent_size > rrdeng_target_data_file_size(ctx))
759 ret = true;
760
761 spinlock_unlock(&datafile->writers.spinlock);
@@ -847,9 +848,14 @@ static void __attribute__((destructor)) destroy_mutex(void) {
848 netdata_mutex_destroy(&mutex);
849 }
850
850 -static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_instance *ctx) {
851 +static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_instance *ctx, uint64_t extent_size) {
852 struct rrdengine_datafile *datafile;
853
854 + // Acquire the mutex at the beginning to make the entire check-and-act atomic
855 + // This prevents the race condition where multiple threads pass the "is full" check
856 + // before any of them increments the position, causing files to grow beyond limits
857 + netdata_mutex_lock(&mutex);
858 +
859 // get the latest datafile
860 netdata_rwlock_rdlock(&ctx->datafiles.rwlock);
861
@@ -860,23 +866,15 @@ static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_
866 spinlock_unlock(&datafile->writers.spinlock);
867 netdata_rwlock_rdunlock(&ctx->datafiles.rwlock);
868
863 - if(datafile_is_full(ctx, datafile)) {
869 + if(datafile_is_full(ctx, datafile, extent_size)) {
870 // remember the datafile we have become writers to
871 struct rrdengine_datafile *old_datafile = datafile;
872
867 - // only 1 datafile creation at a time
868 -
869 - netdata_mutex_lock(&mutex);
870 -
871 - // take the latest datafile again - without this, multiple threads may create multiple files
872 - datafile = get_last_ctx_datafile(ctx, false);
873 -
874 - if(datafile_is_full(ctx, datafile) && create_new_datafile_pair(ctx) == 0)
873 + // Create a new datafile - since we hold the mutex, no other thread can interfere
874 + if(create_new_datafile_pair(ctx) == 0)
875 __atomic_store_n(&ctx->atomic.needs_indexing, true, __ATOMIC_RELAXED);
876
877 - netdata_mutex_unlock(&mutex);
878 -
879 - // get the new latest datafile again, like above
877 + // get the new datafile
878 netdata_rwlock_rdlock(&ctx->datafiles.rwlock);
879 datafile = get_last_ctx_datafile(ctx, true);
880 // become a writer on this datafile, to prevent it from vanishing
@@ -897,6 +895,8 @@ static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_
895 spinlock_unlock(&old_datafile->writers.spinlock);
896 }
897
898 + netdata_mutex_unlock(&mutex);
899 +
900 return datafile;
901 }
902
@@ -1007,7 +1007,8 @@ datafile_extent_build(struct rrdengine_instance *ctx, struct page_descr_with_dat
1007
1008 real_io_size = ALIGN_BYTES_CEILING(size_bytes);
1009
1010 - datafile = get_datafile_to_write_extent(ctx);
1010 + // Pass the extent size so the check can determine if this extent will fit
1011 + datafile = get_datafile_to_write_extent(ctx, real_io_size);
1012 spinlock_lock(&datafile->writers.spinlock);
1013 xt_io_descr->datafile = datafile;
1014 xt_io_descr->pos = datafile->pos;