readdir в AWS EFS не возвращает все файлы в каталоге
После записи большого количества файлов в ряд папок на EFS (10k или около того). Readdir прекращает возвращать все файлы в каждом каталоге.
У меня есть приложение C++, которое в одной части своего процесса генерирует много файлов, и каждому файлу дается символическая ссылка. После этого мне нужно получить список файлов в папке, а затем выбрать подмножество для переименования. Когда я запускаю функцию, которая получает список файлов, она не возвращает все файлы, которые на самом деле там. Этот код отлично работает на моей локальной машине, но на сервере AWS с подключенным диском EFS через некоторое время он перестает работать.
Чтобы устранить эту проблему, я сделал так, чтобы мой код записывал только один файл за раз. Я также настроил свой код для использования getFiles(), чтобы подсчитывать, сколько файлов в папке после записи каждого пакета файлов (около 17 файлов). Когда количество файлов достигает ~950 файлов, getFiles() начинает перечислять ~910 файлов и больше не увеличивается. Когда он записывает файлы, файлы различаются, но довольно малы (2 байта - 300 КБ) и записывают около 200 файлов в секунду. Каждый файл также имеет символическую ссылку, созданную для него.
При чтении и записи файлов я использую posix open(), write(), read() и close(). Я подтвердил, что фактически закрываю все файлы после чтения или записи.
Я пытаюсь выяснить: 1. Почему не работает readdir? Или почему он не перечисляет все файлы? 2. Чем отличается EFS от проблем, которые могут вызывать проблемы?
Это функции, которые я использую для получения списка файлов в папке:
DIR * FileUtil::getDirStream(std::string path) {
bool success = false;
if (!folderExists(path)){
return NULL;
}
DIR * dir = opendir(path.c_str());
success = dir != NULL;
int count = 0;
while(!success){
int fileRetryDelay = BlazingConfig::getInstance()->getFileRetryDelay();
const int sleep_milliseconds = (count+1)*fileRetryDelay;
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_milliseconds));
std::cout<<"Was unable to get Dir stream for "<<path<<std::endl;
dir = opendir(path.c_str());
success = dir != NULL;
count++;
if(count > 6){
break;
}
}
if(success == -1){
std::cout<<"Can't get Dir stream for "<<path<<". Error was: "<<errno<<std::endl;
}
return dir;
}
int FileUtil::getDirEntry(DIR * dirp, struct dirent * & prevDirEntry, struct dirent * & dirEntry){
bool success = false;
if (dirp == NULL){
return -1;
}
int returnCode = readdir_r(dirp, prevDirEntry, &dirEntry);
success = (dirEntry == NULL && returnCode == 0) || dirEntry != NULL;
int count = 0;
while(!success){
int fileRetryDelay = BlazingConfig::getInstance()->getFileRetryDelay();
const int sleep_milliseconds = (count+1)*fileRetryDelay;
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_milliseconds));
std::cout<<"Was unable to get dirent with readdir"<<std::endl;
returnCode = readdir_r(dirp, prevDirEntry, &dirEntry);
success = (dirEntry == NULL && returnCode == 0) || dirEntry != NULL;
count++;
if(count > 6){
break;
}
}
if(success == -1){
std::cout<<"Can't get dirent with readdir. Error was: "<<errno<<std::endl;
}
return returnCode;
}
std::vector<std::string> FileUtil::getFiles(std::string baseFolder){
DIR *dir = getDirStream(baseFolder);
std::vector <std::string> subFolders;
if (dir != NULL) {
struct dirent *prevDirEntry = NULL;
struct dirent *dirEntry = NULL;
int len_entry = offsetof(struct dirent, d_name) + fpathconf(dirfd(dir), _PC_NAME_MAX) + 1;
prevDirEntry = (struct dirent *)malloc(len_entry);
int returnCode = getDirEntry(dir, prevDirEntry, dirEntry);
while (dirEntry != NULL) {
if( dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK){
std::string name(dirEntry->d_name);
subFolders.push_back(name);
}
returnCode = getDirEntry(dir, prevDirEntry, dirEntry);
}
free(prevDirEntry);
closedir (dir);
} else {
std::cout<<"Could not open directory err num is"<<errno<<std::endl;
/* could not open directory */
perror ("");
}
return subFolders;
}
Функции были написаны таким образом, чтобы попытаться быть максимально устойчивыми, так как может быть много потоков, выполняющих файловые операции, я хотел иметь возможность повторять код в случае любых сбоев. К сожалению, когда getFiles() возвращает неправильный результат, это не дает мне никаких признаков сбоя.
Примечание: когда я использую readdir, а не readdir_r, у меня остается та же проблема.