formatter->open_object_section("result");
formatter->dump_string("bucket", bucket_name);
formatter->open_array_section("objects");
+
+ constexpr uint32_t NUM_ENTRIES = 1000;
+ uint16_t attempt = 1;
while (is_truncated) {
RGWRados::ent_map_t result;
- int r = store->getRados()->cls_bucket_list_ordered(
- bucket_info, RGW_NO_SHARD,
- marker, empty_prefix, empty_delimiter,
- 1000, true,
- result, &is_truncated, &cls_filtered, &marker,
- null_yield,
- rgw_bucket_object_check_filter);
+ int r =
+ store->getRados()->cls_bucket_list_ordered(
+ bucket_info, RGW_NO_SHARD,
+ marker, empty_prefix, empty_delimiter,
+ NUM_ENTRIES, true, attempt,
+ result, &is_truncated, &cls_filtered, &marker,
+ null_yield,
+ rgw_bucket_object_check_filter);
+
if (r < 0 && r != -ENOENT) {
cerr << "ERROR: failed operation r=" << r << std::endl;
+ } else if (r == -ENOENT) {
+ break;
}
- if (r == -ENOENT)
- break;
+ if (result.size() < NUM_ENTRIES / 8) {
+ ++attempt;
+ } else if (result.size() > NUM_ENTRIES * 7 / 8 && attempt > 1) {
+ --attempt;
+ }
for (auto iter = result.begin(); iter != result.end(); ++iter) {
rgw_obj_key key = iter->second.key;
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
+#include <sstream>
+
#include <boost/algorithm/string.hpp>
#include <string_view>
}
}
- constexpr int allowed_read_attempts = 2;
- for (int attempt = 0; attempt < allowed_read_attempts; ++attempt) {
+ rgw_obj_index_key prev_marker;
+ uint16_t attempt = 0;
+ while (true) {
+ ldout(cct, 20) << "RGWRados::Bucket::List::" << __func__ <<
+ " beginning attempt=" << ++attempt << dendl;
+
// this loop is generally expected only to have a single
- // iteration; see bottom of loop for early exit
+ // iteration; the standard exit is at the bottom of the loop, but
+ // there's an error condition emergency exit as well
+
+ if (attempt > 1 && !(prev_marker < cur_marker)) {
+ // we've failed to make forward progress
+ ldout(cct, 0) << "RGWRados::Bucket::List::" << __func__ <<
+ ": ERROR marker failed to make forward progress; attempt=" << attempt <<
+ ", prev_marker=" << prev_marker <<
+ ", cur_marker=" << cur_marker << dendl;
+ break;
+ }
+ prev_marker = cur_marker;
ent_map_t ent_map;
ent_map.reserve(read_ahead);
params.delim,
read_ahead + 1 - count,
params.list_versions,
+ attempt,
ent_map,
&truncated,
&cls_filtered,
goto done;
}
+ ldout(cct, 20) << "RGWRados::Bucket::List::" << __func__ <<
+ " adding entry " << entry.key << " to result" << dendl;
+
result->emplace_back(std::move(entry));
count++;
} // eiter for loop
}
} // if older osd didn't do delimiter filtering
- // if we finished listing, or if we're returning at least half the
- // requested entries, that's enough; S3 and swift protocols allow
- // returning fewer than max entries
- if (!truncated || count >= max / 2) {
+ ldout(cct, 20) << "RGWRados::Bucket::List::" << __func__ <<
+ " INFO end of outer loop, truncated=" << truncated <<
+ ", count=" << count << ", attempt=" << attempt << dendl;
+
+ if (!truncated || count >= (max + 1) / 2) {
+ // if we finished listing, or if we're returning at least half the
+ // requested entries, that's enough; S3 and swift protocols allow
+ // returning fewer than max entries
+ break;
+ } else if (attempt > 8 && count >= 1) {
+ // if we've made at least 8 attempts and we have some, but very
+ // few, results, return with what we have
break;
}
ldout(cct, 1) << "RGWRados::Bucket::List::" << __func__ <<
- " INFO ordered bucket listing requires read #" << (2 + attempt) <<
+ " INFO ordered bucket listing requires read #" << (1 + attempt) <<
dendl;
} // read attempt loop
int RGWRados::cls_bucket_list_ordered(RGWBucketInfo& bucket_info,
- int shard_id,
+ const int shard_id,
const rgw_obj_index_key& start_after,
const string& prefix,
const string& delimiter,
- uint32_t num_entries,
- bool list_versions,
+ const uint32_t num_entries,
+ const bool list_versions,
+ const uint16_t attempt,
ent_map_t& m,
bool* is_truncated,
bool* cls_filtered,
optional_yield y,
check_filter_t force_check_filter)
{
- ldout(cct, 10) << "cls_bucket_list_ordered " << bucket_info.bucket <<
- " start_after " << start_after.name << "[" << start_after.instance <<
- "] num_entries " << num_entries << dendl;
+ ldout(cct, 10) << "RGWRados::" << __func__ << ": " << bucket_info.bucket <<
+ " start_after=\"" << start_after.name <<
+ "[" << start_after.instance <<
+ "]\", prefix=\"" << prefix <<
+ "\" num_entries=" << num_entries <<
+ ", list_versions=" << list_versions <<
+ ", attempt=" << attempt << dendl;
RGWSI_RADOS::Pool index_pool;
// key - oid (for different shards if there is any)
}
const uint32_t shard_count = oids.size();
- const uint32_t num_entries_per_shard =
- calc_ordered_bucket_list_per_shard(num_entries, shard_count);
+ uint32_t num_entries_per_shard;
+ if (attempt == 0) {
+ num_entries_per_shard =
+ calc_ordered_bucket_list_per_shard(num_entries, shard_count);
+ } else if (attempt <= 11) {
+ // we'll max out the exponential multiplication factor at 1024 (2<<10)
+ num_entries_per_shard =
+ std::min(num_entries,
+ (uint32_t(1 << (attempt - 1)) *
+ calc_ordered_bucket_list_per_shard(num_entries, shard_count)));
+ } else {
+ num_entries_per_shard = num_entries;
+ }
- ldout(cct, 10) << __func__ << " request from each of " << shard_count <<
+ ldout(cct, 10) << "RGWRados::" << __func__ <<
+ " request from each of " << shard_count <<
" shard(s) for " << num_entries_per_shard << " entries to get " <<
num_entries << " total entries" << dendl;
return r;
}
+ auto result_info =
+ [](const map<int, struct rgw_cls_list_ret>& m) -> std::string {
+ std::stringstream out;
+ out << "{ size:" << m.size() << ", entries:[";
+ for (const auto& i : m) {
+ out << " { " << i.first << ", " << i.second.dir.m.size() << " },";
+ }
+ out << "] }";
+ return out.str();
+ };
+
+ ldout(cct, 20) << "RGWRados::" << __func__ <<
+ " CLSRGWIssueBucketList() result=" <<
+ result_info(list_results) << dendl;
+
// create a list of iterators that are used to iterate each shard
vector<RGWRados::ent_map_t::iterator> vcurrents;
vector<RGWRados::ent_map_t::iterator> vends;
const string& name = vcurrents[pos]->first;
struct rgw_bucket_dir_entry& dirent = vcurrents[pos]->second;
+ ldout(cct, 20) << "RGWRados::" << __func__ << " currently processing " <<
+ dirent.key << " from shard " << pos << dendl;
+
bool force_check =
force_check_filter && force_check_filter(dirent.key.name);
} else {
r = 0;
}
+
if (r >= 0) {
- ldout(cct, 10) << "RGWRados::cls_bucket_list_ordered: got " <<
+ ldout(cct, 10) << "RGWRados::" << __func__ << ": got " <<
dirent.key.name << "[" << dirent.key.instance << "]" << dendl;
m[name] = std::move(dirent);
++count;
+ } else {
+ ldout(cct, 10) << "RGWRados::" << __func__ << ": skipping " <<
+ dirent.key.name << "[" << dirent.key.instance << "]" << dendl;
}
// refresh the candidates map
}
} // while we haven't provided requested # of result entries
- // suggest updates if there is any
+ // suggest updates if there are any
for (auto& miter : updates) {
if (miter.second.length()) {
ObjectWriteOperation o;
}
}
+ ldout(cct, 20) << "RGWRados::" << __func__ <<
+ ": returning, count=" << count << ", is_truncated=" << *is_truncated <<
+ dendl;
+
if (*is_truncated && count < num_entries) {
ldout(cct, 10) << "RGWRados::" << __func__ <<
": INFO requested " << num_entries << " entries but returning " <<
count << ", which is truncated" << dendl;
}
- if (pos >= 0)
+ if (pos >= 0) {
*last_entry = std::move((--vcurrents[pos])->first);
+ ldout(cct, 20) << "RGWRados::" << __func__ <<
+ ": returning, last_entry=" << *last_entry << dendl;
+ } else {
+ ldout(cct, 20) << "RGWRados::" << __func__ <<
+ ": returning, last_entry NOT SET" << dendl;
+ }
return 0;
}