From e99b996beaaab2b4c1053f222e6ea15b99ab113b Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Thu, 5 Nov 2020 10:47:57 -0500 Subject: [PATCH] journal: fix reading omaps from objects with large key counts The implementation of getOMapValues assumed that the number of key-value pairs assigned to the object would be close to the number of keys being requested. When the number of keys on the object exceeded the "listExcess" value the function would fail to read additional keys even if they existed in the omap. This change sets a large fixed "chunk size" value and keeps reading key-value pairs as long as the callback gets called and increments the numKeys counter. Signed-off-by: John Mulligan (cherry picked from commit 8a41cd03a5813a9ffe046b86c660e4ea99377628) --- internal/journal/omap.go | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/internal/journal/omap.go b/internal/journal/omap.go index ed32be3d1..825f4a98a 100644 --- a/internal/journal/omap.go +++ b/internal/journal/omap.go @@ -25,9 +25,10 @@ import ( "github.com/ceph/go-ceph/rados" ) -// listExcess is the number of false-positive key-value pairs we will -// accept from ceph when getting omap values. -var listExcess = 32 +// chunkSize is the number of key-value pairs that will be fetched in +// one call. This is set fairly large to avoid calling into ceph APIs +// over and over. +const chunkSize int64 = 512 func getOMapValues( ctx context.Context, @@ -51,15 +52,26 @@ func getOMapValues( for i := range keys { want[keys[i]] = true } + numKeys := uint64(0) + startAfter := "" + for { + prevNumKeys := numKeys + err = ioctx.ListOmapValues( + oid, startAfter, prefix, chunkSize, + func(key string, value []byte) { + numKeys++ + startAfter = key + if want[key] { + results[key] = string(value) + } + }, + ) + // if we hit an error, or no new keys were seen, exit the loop + if err != nil || numKeys == prevNumKeys { + break + } + } - err = ioctx.ListOmapValues( - oid, "", prefix, int64(len(want)+listExcess), - func(key string, value []byte) { - if want[key] { - results[key] = string(value) - } - }, - ) if err != nil { if errors.Is(err, rados.ErrNotFound) { util.ErrorLog(ctx, "omap not found (pool=%q, namespace=%q, name=%q): %v",