#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;
+ rgw_obj_index_key prev_marker;
string skip_after_delim;
- for (int attempt = 0; attempt < allowed_read_attempts; ++attempt) {
+ 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; 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;
+
std::map<string, rgw_bucket_dir_entry> ent_map;
int r = store->cls_bucket_list_ordered(target->get_bucket_info(),
shard_id,
cur_prefix,
read_ahead + 1 - count,
params.list_versions,
+ attempt,
ent_map,
&truncated,
&cur_marker);
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 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 rgw_obj_index_key& start,
+ const int shard_id,
+ const rgw_obj_index_key& start_after,
const string& prefix,
- uint32_t num_entries,
- bool list_versions,
+ const uint32_t num_entries,
+ const bool list_versions,
+ const uint16_t attempt,
map<string, rgw_bucket_dir_entry>& m,
bool *is_truncated,
rgw_obj_index_key *last_entry,
bool (*force_check_filter)(const string& name))
{
- ldout(cct, 10) << "cls_bucket_list_ordered " << bucket_info.bucket <<
- " start " << start.name << "[" << start.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;
librados::IoCtx index_ctx;
// 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;
map<int, struct rgw_cls_list_ret> list_results;
- cls_rgw_obj_key start_key(start.name, start.instance);
+ cls_rgw_obj_key start_key(start_after.name, start_after.instance);
r = CLSRGWIssueBucketList(index_ctx, start_key, prefix, num_entries_per_shard,
list_versions, oids, list_results,
cct->_conf->rgw_bucket_index_max_aio)();
const string& name = vcurrents[pos]->first;
struct rgw_bucket_dir_entry& dirent = vcurrents[pos]->second;
- bool force_check = force_check_filter &&
- force_check_filter(dirent.key.name);
+ 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);
+
if ((!dirent.exists && !dirent.is_delete_marker()) ||
!dirent.pending_map.empty() ||
force_check) {
} 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 " <<
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;