-
Notifications
You must be signed in to change notification settings - Fork 65
[ML] Refactor model snapshot writing to use concurrent writer #12
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
[ML] Refactor model snapshot writing to use concurrent writer #12
Conversation
This refactors the writing of the model snapshot document and model size stats document out of CJsonOutputWriter. It achieves the following improvements: - model snapshot documents do not have to be queued up. They can be written directly leveraging the safe concurrent writer that was implemented for forecasts. This minimizes the delay of a model snapshot being written which in failure cases may result into having a more recent snapshot available. - better separation of concerns and a smaller CJsonOutputWriter class. - simplifies adding new fields in the model snapshot document as needed for writing the minimum compatible version.
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 think this is a good idea. Left some additional suggestions and corrections for typos.
const model::CResourceMonitor::SResults &modelSizeStats, | ||
const std::string &normalizerState, | ||
core_t::TTime latestRecordTime, | ||
core_t::TTime latestFinalResultTime); |
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.
Just a thought, but you don't actually need to provide a constructor for this pod. You can just use a list initialiser and provide the arguments in the order in which they are declared in the object, i.e. SModelSnapshotReport report{time, description, ...}
. The only downside is if someone reorders the fields in the class definition, but this should generally fail to compile since the types must be convertible to one another. That would be an odd thing to do as well.
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.
Done
static const std::string BUCKET_ALLOCATION_FAILURES_COUNT; | ||
static const std::string MEMORY_STATUS; | ||
static const std::string TIMESTAMP; | ||
static const std::string LOG_TIME; |
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.
These don't appear to be needed outside of this object. In which case, I generally prefer to just define them in an unnamed namespace in the cc file. It means if we want to write new fields we don't have to touch the header and also tidies up the class definition.
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.
Done
core::CRapidJsonConcurrentLineWriter &writer); | ||
}; | ||
|
||
|
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.
Extra new line.
static const std::string LATEST_RECORD_TIME; | ||
static const std::string LATEST_RESULT_TIME; | ||
static const std::string QUANTILES; | ||
static const std::string QUANTILE_STATE; |
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.
As above.
lib/api/CModelSizeStatsJsonWriter.cc
Outdated
writer.EndObject(); | ||
} | ||
|
||
|
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.
Extra new line.
writer.String(print(results.s_MemoryStatus)); | ||
|
||
writer.String(TIMESTAMP); | ||
writer.Int64(results.s_BucketStartTime * 1000); |
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.
There are quite a few places where we magically multiply times by 1000 sometimes (correctly) casting to int64_t
and sometimes not. Might be nice to add a utility function which does this; maybe in core::CPersistUtils
.
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.
Maybe static int64_t core::CTimeUtils::toJavaTimestamp(core_t::TTime)
?
Or alternatively static int64_t core::CTimeUtils::toEpochMs(core_t::TTime)
.
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.
Happy to do this but I'll open another PR for it.
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.
Yes, that's a better suggestion. I up vote static int64_t core::CTimeUtils::toEpochMs(core_t::TTime)
.
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.
Also, makes sense to defer to separate PR. There may well be other cases of this as well.
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 only drawback of the add{String/Int/Uint/..}FieldToObj
methods is that you have to construct an extra layer of C++ objects to represent the JSON document in memory before writing it as a text JSON document. We do this in cases where we need to return to the documents later to add extra fields. But in cases where a single document can be written start-to-end in one method it's an unnecessary overhead. For documents written as infrequently as model snapshots I don't think the performance difference will be measurable, but I wouldn't want to set a precedent that we only write JSON by constructing documents in memory.
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.
Good point! (So I could simplify writing forecast results, too.)
What about a writer.Time(int64_t time)
(to be implemented in CRapidJsonWriterBase.h). I still like the idea of moving the logic into the writer as we have addTimeFieldToObj already. IMHO that is more consistent to that.
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.
Yes, sure we could have writer.Time(core_t::TTime time)
and then all the methods that multiply by 1000 could encapsulate that logic by deferring to int64_t core::CTimeUtils::toEpochMs(core_t::TTime)
. That will make it easier if we ever change the time units we use in the C++ code.
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.
👍 exactly
(I was only unsure if core_t::TTime or int64_t as the writer class is somewhat already in rapidjson space, but its hard to draw the line, so core_t::TTime is fine)
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.
Raised #16
lib/api/CModelSizeStatsJsonWriter.cc
Outdated
/* | ||
* ELASTICSEARCH CONFIDENTIAL | ||
* | ||
* Copyright (c) 2016 Elasticsearch BV. All Rights Reserved. |
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.
2018, although not sure how this will interact with changes to license header.
/* | ||
* ELASTICSEARCH CONFIDENTIAL | ||
* | ||
* Copyright (c) 2016 Elasticsearch BV. All Rights Reserved. |
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.
2018, although not sure how this will interact with changes to license header.
/* | ||
* ELASTICSEARCH CONFIDENTIAL | ||
* | ||
* Copyright (c) 2016 Elasticsearch BV. All Rights Reserved. |
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.
2018, although not sure how this will interact with changes to license header.
|
||
#include <core/CRapidJsonConcurrentLineWriter.h> | ||
|
||
#include <core/CNonInstantiatable.h> |
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.
combine with 2 lines above and sort?
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.
Also, these should be in lexicographical order.
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.
Done
LOG_DEBUG("Persist complete with description: " << description); | ||
snapshotIdOut = snapshotIdIn; | ||
numDocsOut = numDocsIn; | ||
LOG_INFO("Persist complete with description: " << modelSnapshotReport.s_Description); |
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.
you raised the log level, by intent or debug leftover?
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.
Fixed
LOG_DEBUG("Persist complete with description: " << description); | ||
snapshotIdOut = snapshotIdIn; | ||
numDocsOut = numDocsIn; | ||
LOG_INFO("Persist complete with description: " << modelSnapshotReport.s_Description); |
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.
DEBUG->INFO ?
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.
Done
All comments have been addressed. Could you guys have another look? |
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.
LGTM
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.
LGTM
and remove some blank lines
This refactors the writing of the model snapshot document and model size stats document out of CJsonOutputWriter. It achieves the following improvements: - model snapshot documents do not have to be queued up. They can be written directly leveraging the safe concurrent writer that was implemented for forecasts. This minimizes the delay of a model snapshot being written which in failure cases may result into having a more recent snapshot available. - better separation of concerns and a smaller CJsonOutputWriter class. - simplifies adding new fields in the model snapshot document as needed for writing the minimum compatible version.
This refactors the writing of the model snapshot document and model size stats document out of CJsonOutputWriter. It achieves the following improvements: - model snapshot documents do not have to be queued up. They can be written directly leveraging the safe concurrent writer that was implemented for forecasts. This minimizes the delay of a model snapshot being written which in failure cases may result into having a more recent snapshot available. - better separation of concerns and a smaller CJsonOutputWriter class. - simplifies adding new fields in the model snapshot document as needed for writing the minimum compatible version.
This refactors the writing of the model snapshot document and model size stats document out of CJsonOutputWriter. It achieves the following improvements: - model snapshot documents do not have to be queued up. They can be written directly leveraging the safe concurrent writer that was implemented for forecasts. This minimizes the delay of a model snapshot being written which in failure cases may result into having a more recent snapshot available. - better separation of concerns and a smaller CJsonOutputWriter class. - simplifies adding new fields in the model snapshot document as needed for writing the minimum compatible version.
This refactors the writing of the model snapshot document and
model size stats document out of CJsonOutputWriter.
It achieves the following improvements:
can be written directly leveraging the safe concurrent writer
that was implemented for forecasts. This minimizes the delay
of a model snapshot being written which in failure cases may
result into having a more recent snapshot available.
needed for writing the minimum compatible version.