The problem with _get_candidate_file_descriptor_ranges() is that in _total_file_descriptor_set a set of the size is initialized.
While it works well for smaller limits, like 2048, the limit that I've encountered was 1073741816. This resulted in a simple import statement import daemon triggering the attempt to create a full set of numbers of that size, which quickly ate all of 64GBs of memory that I had and crashed :)
Based on the only usage of _total_file_descriptor_set which is in a helper _get_candidate_file_descriptor_ranges function, I don't think it is actually needed. We operate with ranges, and not need an actual list of descriptors to be stored in memory. In my first attempt to tackle this, I just replaced the logic of iteration to use a calculated range instead of a set object. This helped with memory footprint, but there still was a performance bottleneck - the time to iterate over 1B items in Python is still not fast (for ex. the newly test would took hours).
So, instead of iterating over the candidate descriptors, the _get_candidate_file_descriptor_ranges got rewritten to iterate over exclusions. Contrary to the total range, for this one we can assume that it will be relatively small (almost certainly less that 1000). The logic of building ranges didn't change drastically either - we build ranges, but instead of keeping track and updating the last added range, the not-yet-processed part is being tracked and updated after each excluded descriptor that can split it into two ranges is processed.
A number of helper functions are cleaned up as they are no longer needed.
The problem with is initialized.
_get_candidate_file_descriptor_ranges()is that in_total_file_descriptor_seta set of the sizeWhile it works well for smaller limits, like 2048, the limit that I've encountered was 1073741816. This resulted in a simple import statement
import daemontriggering the attempt to create a full set of numbers of that size, which quickly ate all of 64GBs of memory that I had and crashed :)Based on the only usage of
_total_file_descriptor_setwhich is in a helper_get_candidate_file_descriptor_rangesfunction, I don't think it is actually needed. We operate with ranges, and not need an actual list of descriptors to be stored in memory. In my first attempt to tackle this, I just replaced the logic of iteration to use a calculated range instead of a set object. This helped with memory footprint, but there still was a performance bottleneck - the time to iterate over 1B items in Python is still not fast (for ex. the newly test would took hours).So, instead of iterating over the candidate descriptors, the
_get_candidate_file_descriptor_rangesgot rewritten to iterate over exclusions. Contrary to the total range, for this one we can assume that it will be relatively small (almost certainly less that 1000). The logic of building ranges didn't change drastically either - we build ranges, but instead of keeping track and updating the last added range, the not-yet-processed part is being tracked and updated after each excluded descriptor that can split it into two ranges is processed.A number of helper functions are cleaned up as they are no longer needed.