-
Notifications
You must be signed in to change notification settings - Fork 820
flush boltdb files to object stores #1533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
flush boltdb files to object stores #1533
Conversation
b8c1f10
to
62ee90a
Compare
e6b0f42
to
9bd6667
Compare
4a33d2f
to
42725ee
Compare
c667894
to
ac777ef
Compare
@sandeepsukhani Could you rebase |
files are stored in folder per periodic table and are named after ingester flushed every 15 mins to make index available to other services files are also flushed before ingester stops to avoid any data loss new stores can be implemented easily ingester to also query store when using boltdb Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
…re to come Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
… or not Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
5bb8140
to
afee846
Compare
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Signed-off-by: Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a relatively quick first pass. I would have several minor comments which I've skipped so far, so that we can focus on the most important things first. My main concern is about the safety of changing BoltDB files on a already opened BoltDB. I assume you've already investigated this and it's safe, but I can't find any detailed comment about it in the code, so I would like to better understand how we can safely change files under the hood to BoltDB while still working on it (ie. querying it).
|
||
// ArchiveStoreClient define all the methods that a store needs to implement for managing objects | ||
type ArchiveStoreClient interface { | ||
GetObject(ctx context.Context, objectName string) ([]byte, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable objectName
would be more correct to be named objectKey
because it's actually used as the object key (made of prefix + object name). I would suggest this renaming across the change set (both interface and implementations).
Same comment applies to PutObject()
too.
|
||
type StoreConfig struct { | ||
Store string `yaml:"store"` | ||
AWSStorageConfig aws.StorageConfig `yaml:"aws"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate we have to use aws.StorageConfig
because it also brings a bunch of config options related to DynamoDB we don't really need. In the aws
package we can:
- Introduce
S3Config
(like we haveGCSConfig
) - Move
S3
,BucketNames
andS3ForcePathStyle
toS3Config
- Inline embed
S3Config
intoStorageConfig
so that we don't introduce a breaking change (you can do it withS3 S3Config `yaml:"inline"`
) - Change
NewS3ObjectClient
to pickS3Config
in input, instead ofStorageConfig
Remember to re-run make doc
so that we see the updated config.
|
||
// Put object into the store | ||
func (a S3ObjectClient) PutObject(ctx context.Context, objectName string, object []byte) error { | ||
return instrument.CollectedRequest(ctx, "S3.Put", s3RequestDuration, instrument.ErrorCode, func(ctx context.Context) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
S3.Put
> S3.PutObject
} | ||
|
||
// ArchiveStoreClient define all the methods that a store needs to implement for managing objects | ||
type ArchiveStoreClient interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GetObject()
and PutObject()
work with []byte
. This means we need to load the entire index in-memory while uploading/downloading. What's the expected index size for a large cluster in production?
I'm asking because this could be an issue, and we may want to actually work with io.Reader
interfaces (ie. see Thanos S3 objstore client)
return aDB, nil | ||
} | ||
|
||
b.archivedDbsMtx.Lock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lock blocks any index query, even of DBs different than name
, until this function returns, due to archiver.Sync()
and openBoltdbFile()
. Have you thought about this?
return nil | ||
} | ||
|
||
func (a *Archiver) syncTable(ctx context.Context, tableName string, sendUpdates bool) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this function we do add, update and delete files for an open BoltDB table. Is it a safe operation? Is it safe doing it while, in the meanwhile, we may query it?
return nil | ||
} | ||
|
||
func (a *Archiver) syncTable(ctx context.Context, tableName string, sendUpdates bool) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need this sendUpdates
instead of always doing it?
result.Timeseries = append(result.Timeseries, ts) | ||
return nil | ||
}, nil, 0) | ||
i.metrics.queriedSeries.Observe(float64(numSeries)) | ||
|
||
if i.cfg.QueryStore { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain me why we do want all this logic, please?
return err | ||
} | ||
|
||
buf, err := ioutil.ReadAll(f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're reading a file while BoltDB may simultaneously write on it. How can we guarantee the uploaded file is not corrupted?
|
||
// We would use ingester name and startup timestamp for naming files while uploading so that | ||
// ingester does not override old files when using same id | ||
ingesterNameAndStartUpTs string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may rename it to something like ingesterUniqueID
(the fact it's built upon ingester name + timestamp is an implementation detail, but what we need to store in the variable is a unique ID for the ingester).
Since Cortex now has TSDB support, this is not needed. |
This can be useful for running cortex using just boltdb and gcs/s3.
Few details about implementation: