summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2018-08-23 15:16:56 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2018-09-17 18:39:09 -0400
commita845bcf20059b548ca8f1b4a51b8b4bd0512fa59 (patch)
treea2dac7851db28c31b4535884467a79fcb4755c2c
parent32eccfba49356f1ebe769f57c02ec39286f26843 (diff)
downloadmongo-a845bcf20059b548ca8f1b4a51b8b4bd0512fa59.tar.gz
SERVER-34864 FTDC should ignore irrelevant schema changes
(cherry picked from commit 078d6b49548d90880556af6f55e3baf8b4709917)
-rw-r--r--src/mongo/db/ftdc/compressor.cpp7
-rw-r--r--src/mongo/db/ftdc/compressor_test.cpp136
-rw-r--r--src/mongo/db/ftdc/controller_test.cpp4
-rw-r--r--src/mongo/db/ftdc/file_manager_test.cpp4
-rw-r--r--src/mongo/db/ftdc/file_writer_test.cpp2
-rw-r--r--src/mongo/db/ftdc/ftdc_test.cpp40
-rw-r--r--src/mongo/db/ftdc/ftdc_test.h24
-rw-r--r--src/mongo/db/ftdc/util.cpp66
-rw-r--r--src/mongo/db/ftdc/util.h5
9 files changed, 257 insertions, 31 deletions
diff --git a/src/mongo/db/ftdc/compressor.cpp b/src/mongo/db/ftdc/compressor.cpp
index ecf9c7ece6c..5be24ceb1f7 100644
--- a/src/mongo/db/ftdc/compressor.cpp
+++ b/src/mongo/db/ftdc/compressor.cpp
@@ -45,7 +45,12 @@ using std::swap;
StatusWith<boost::optional<std::tuple<ConstDataRange, FTDCCompressor::CompressorState, Date_t>>>
FTDCCompressor::addSample(const BSONObj& sample, Date_t date) {
if (_referenceDoc.isEmpty()) {
- FTDCBSONUtil::extractMetricsFromDocument(sample, sample, &_metrics);
+ auto swMatchesReference =
+ FTDCBSONUtil::extractMetricsFromDocument(sample, sample, &_metrics);
+ if (!swMatchesReference.isOK()) {
+ return swMatchesReference.getStatus();
+ }
+
_reset(sample, date);
return {boost::none};
}
diff --git a/src/mongo/db/ftdc/compressor_test.cpp b/src/mongo/db/ftdc/compressor_test.cpp
index 0c01bd58040..5050245b468 100644
--- a/src/mongo/db/ftdc/compressor_test.cpp
+++ b/src/mongo/db/ftdc/compressor_test.cpp
@@ -48,17 +48,17 @@ namespace mongo {
ASSERT_TRUE(st.isOK()); \
ASSERT_FALSE(st.getValue().is_initialized());
-#define ASSERT_SCHEMA_CHANGED(st) \
- ASSERT_TRUE(st.isOK()); \
- ASSERT_TRUE(std::get<1>(st.getValue().get()) == \
- FTDCCompressor::CompressorState::kSchemaChanged); \
- ASSERT_TRUE(st.getValue().is_initialized());
-
-#define ASSERT_FULL(st) \
- ASSERT_TRUE(st.isOK()); \
- ASSERT_TRUE(std::get<1>(st.getValue().get()) == \
- FTDCCompressor::CompressorState::kCompressorFull); \
- ASSERT_TRUE(st.getValue().is_initialized());
+#define ASSERT_SCHEMA_CHANGED(st) \
+ ASSERT_TRUE(st.isOK()); \
+ ASSERT_TRUE(st.getValue().is_initialized()); \
+ ASSERT_TRUE(std::get<1>(st.getValue().get()) == \
+ FTDCCompressor::CompressorState::kSchemaChanged);
+
+#define ASSERT_FULL(st) \
+ ASSERT_TRUE(st.isOK()); \
+ ASSERT_TRUE(st.getValue().is_initialized()); \
+ ASSERT_TRUE(std::get<1>(st.getValue().get()) == \
+ FTDCCompressor::CompressorState::kCompressorFull);
// Sanity check
TEST(FTDCCompressor, TestBasic) {
@@ -125,7 +125,8 @@ TEST(FTDCCompressor, TestStrings) {
*/
class TestTie {
public:
- TestTie() : _compressor(&_config) {}
+ TestTie(FTDCValidationMode mode = FTDCValidationMode::kStrict)
+ : _compressor(&_config), _mode(mode) {}
~TestTie() {
validate(boost::none);
@@ -169,7 +170,7 @@ public:
list = sw.getValue();
}
- ValidateDocumentList(list, _docs);
+ ValidateDocumentList(list, _docs, _mode);
}
private:
@@ -177,6 +178,7 @@ private:
FTDCConfig _config;
FTDCCompressor _compressor;
FTDCDecompressor _decompressor;
+ FTDCValidationMode _mode;
};
// Test various schema changes
@@ -340,6 +342,114 @@ TEST(FTDCCompressor, TestSchemaChanges) {
ASSERT_SCHEMA_CHANGED(st);
}
+// Test various schema changes with strings
+TEST_F(FTDCCompressorTest, TestStringSchemaChanges) {
+ TestTie c(FTDCValidationMode::kWeak);
+
+ auto st = c.addSample(BSON("str1"
+ << "joe"
+ << "int1"
+ << 42));
+ ASSERT_HAS_SPACE(st);
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int1"
+ << 45));
+ ASSERT_HAS_SPACE(st);
+
+ // Add string field
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "str2"
+ << "smith"
+ << "int1"
+ << 47));
+ ASSERT_HAS_SPACE(st);
+
+ // Reset schema by renaming a int field
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "str2"
+ << "smith"
+ << "int2"
+ << 48));
+ ASSERT_SCHEMA_CHANGED(st);
+
+ // Remove string field
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int2"
+ << 49));
+ ASSERT_HAS_SPACE(st);
+
+
+ // Add string field as last element
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int2"
+ << 50
+ << "str3"
+ << "bar"));
+ ASSERT_HAS_SPACE(st);
+
+ // Reset schema by renaming a int field
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int1"
+ << 51
+ << "str3"
+ << "bar"));
+ ASSERT_SCHEMA_CHANGED(st);
+
+ // Remove string field as last element
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int1"
+ << 52));
+ ASSERT_HAS_SPACE(st);
+
+
+ // Add 2 string fields
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "str2"
+ << "smith"
+ << "str3"
+ << "foo"
+ << "int1"
+ << 53));
+ ASSERT_HAS_SPACE(st);
+
+ // Reset schema by renaming a int field
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "str2"
+ << "smith"
+ << "str3"
+ << "foo"
+ << "int2"
+ << 54));
+ ASSERT_SCHEMA_CHANGED(st);
+
+ // Remove 2 string fields
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int2"
+ << 55));
+ ASSERT_HAS_SPACE(st);
+
+ // Change string to number
+ st = c.addSample(BSON("str1" << 12 << "int1" << 56));
+ ASSERT_SCHEMA_CHANGED(st);
+
+ // Change number to string
+ st = c.addSample(BSON("str1"
+ << "joe"
+ << "int1"
+ << 67));
+ ASSERT_SCHEMA_CHANGED(st);
+}
+
// Ensure changing between the various number formats is considered compatible
TEST(FTDCCompressor, TestNumbersCompat) {
TestTie c;
diff --git a/src/mongo/db/ftdc/controller_test.cpp b/src/mongo/db/ftdc/controller_test.cpp
index c75a3fe44d6..94b028845d4 100644
--- a/src/mongo/db/ftdc/controller_test.cpp
+++ b/src/mongo/db/ftdc/controller_test.cpp
@@ -202,7 +202,7 @@ TEST(FTDCControllerTest, TestFull) {
auto alog = files[0];
- ValidateDocumentList(alog, allDocs);
+ ValidateDocumentList(alog, allDocs, FTDCValidationMode::kStrict);
}
// Test we can start and stop the controller in quick succession, make sure it succeeds without
@@ -274,7 +274,7 @@ TEST(FTDCControllerTest, TestStartAsDisabled) {
auto alog = files[0];
- ValidateDocumentList(alog, allDocs);
+ ValidateDocumentList(alog, allDocs, FTDCValidationMode::kStrict);
}
} // namespace mongo
diff --git a/src/mongo/db/ftdc/file_manager_test.cpp b/src/mongo/db/ftdc/file_manager_test.cpp
index 6c2e5c220a6..8ac115df584 100644
--- a/src/mongo/db/ftdc/file_manager_test.cpp
+++ b/src/mongo/db/ftdc/file_manager_test.cpp
@@ -378,11 +378,11 @@ TEST(FTDCFileManagerTest, TestNormalCrashInterim) {
// Validate old file
std::vector<BSONObj> docs1 = {mdoc1, sdoc1, sdoc1};
- ValidateDocumentList(files[0], docs1);
+ ValidateDocumentList(files[0], docs1, FTDCValidationMode::kStrict);
// Validate new file
std::vector<BSONObj> docs2 = {sdoc2, sdoc2, sdoc2, sdoc2};
- ValidateDocumentList(files[1], docs2);
+ ValidateDocumentList(files[1], docs2, FTDCValidationMode::kStrict);
}
} // namespace mongo
diff --git a/src/mongo/db/ftdc/file_writer_test.cpp b/src/mongo/db/ftdc/file_writer_test.cpp
index 138d7c850f6..a182d52fdfe 100644
--- a/src/mongo/db/ftdc/file_writer_test.cpp
+++ b/src/mongo/db/ftdc/file_writer_test.cpp
@@ -196,7 +196,7 @@ private:
_writer.close();
- ValidateDocumentList(_path, _docs);
+ ValidateDocumentList(_path, _docs, FTDCValidationMode::kStrict);
}
private:
diff --git a/src/mongo/db/ftdc/ftdc_test.cpp b/src/mongo/db/ftdc/ftdc_test.cpp
index 7e4020b3979..49be73bb8cc 100644
--- a/src/mongo/db/ftdc/ftdc_test.cpp
+++ b/src/mongo/db/ftdc/ftdc_test.cpp
@@ -48,7 +48,23 @@
namespace mongo {
-void ValidateDocumentList(const boost::filesystem::path& p, const std::vector<BSONObj>& docs) {
+namespace {
+
+BSONObj filteredFTDCCopy(const BSONObj& obj) {
+ BSONObjBuilder builder;
+ for (const auto& f : obj) {
+ if (FTDCBSONUtil::isFTDCType(f.type())) {
+ builder.append(f);
+ }
+ }
+ return builder.obj();
+}
+} // namespace
+
+
+void ValidateDocumentList(const boost::filesystem::path& p,
+ const std::vector<BSONObj>& docs,
+ FTDCValidationMode mode) {
FTDCFileReader reader;
ASSERT_OK(reader.open(p));
@@ -62,20 +78,32 @@ void ValidateDocumentList(const boost::filesystem::path& p, const std::vector<BS
ASSERT_OK(sw);
- ValidateDocumentList(list, docs);
+ ValidateDocumentList(list, docs, mode);
}
-void ValidateDocumentList(const std::vector<BSONObj>& docs1, const std::vector<BSONObj>& docs2) {
+void ValidateDocumentList(const std::vector<BSONObj>& docs1,
+ const std::vector<BSONObj>& docs2,
+ FTDCValidationMode mode) {
ASSERT_EQUALS(docs1.size(), docs2.size());
auto ai = docs1.begin();
auto bi = docs2.begin();
while (ai != docs1.end() && bi != docs2.end()) {
- if (SimpleBSONObjComparator::kInstance.evaluate(*ai != *bi)) {
- std::cout << *ai << " vs " << *bi << std::endl;
- ASSERT_BSONOBJ_EQ(*ai, *bi);
+ if (mode == FTDCValidationMode::kStrict) {
+ if (SimpleBSONObjComparator::kInstance.evaluate(*ai != *bi)) {
+ std::cout << *ai << " vs " << *bi << std::endl;
+ ASSERT_BSONOBJ_EQ(*ai, *bi);
+ }
+ } else {
+ BSONObj left = filteredFTDCCopy(*ai);
+ BSONObj right = filteredFTDCCopy(*bi);
+ if (SimpleBSONObjComparator::kInstance.evaluate(left != right)) {
+ std::cout << left << " vs " << right << std::endl;
+ ASSERT_BSONOBJ_EQ(left, right);
+ }
}
+
++ai;
++bi;
}
diff --git a/src/mongo/db/ftdc/ftdc_test.h b/src/mongo/db/ftdc/ftdc_test.h
index afbca103b4f..d3b7b52d647 100644
--- a/src/mongo/db/ftdc/ftdc_test.h
+++ b/src/mongo/db/ftdc/ftdc_test.h
@@ -34,18 +34,38 @@
namespace mongo {
/**
+ * Validation mode for tests, strict by default
+ */
+enum class FTDCValidationMode {
+ /**
+ * Compare BSONObjs exactly.
+ */
+ kStrict,
+
+ /**
+ * Compare BSONObjs by only comparing types FTDC compares about. FTDC ignores somes changes in
+ * the shapes of documents and therefore no longer reconstructs the shapes of documents exactly.
+ */
+ kWeak,
+};
+
+/**
* Validate the documents in a file match the specified vector.
*
* Unit Test ASSERTs if there is mismatch.
*/
-void ValidateDocumentList(const boost::filesystem::path& p, const std::vector<BSONObj>& docs);
+void ValidateDocumentList(const boost::filesystem::path& p,
+ const std::vector<BSONObj>& docs,
+ FTDCValidationMode mode);
/**
* Validate that two lists of documents are equal.
*
* Unit Test ASSERTs if there is mismatch.
*/
-void ValidateDocumentList(const std::vector<BSONObj>& docs1, const std::vector<BSONObj>& docs2);
+void ValidateDocumentList(const std::vector<BSONObj>& docs1,
+ const std::vector<BSONObj>& docs2,
+ FTDCValidationMode mode);
/**
* Delete a file if it exists.
diff --git a/src/mongo/db/ftdc/util.cpp b/src/mongo/db/ftdc/util.cpp
index 243ba90b666..ecfc4db5f37 100644
--- a/src/mongo/db/ftdc/util.cpp
+++ b/src/mongo/db/ftdc/util.cpp
@@ -123,6 +123,47 @@ namespace FTDCBSONUtil {
namespace {
+/**
+ * Iterate a BSONObj but only return fields that have types that FTDC cares about.
+ */
+class FTDCBSONObjIterator {
+public:
+ FTDCBSONObjIterator(const BSONObj& obj) : _iterator(obj) {
+ advance();
+ }
+
+ bool more() {
+ return !_current.eoo();
+ }
+
+ BSONElement next() {
+ auto ret = _current;
+ advance();
+ return ret;
+ }
+
+private:
+ /**
+ * Find the next element that is a valid FTDC type.
+ */
+ void advance() {
+ _current = BSONElement();
+
+ while (_iterator.more()) {
+
+ auto elem = _iterator.next();
+ if (isFTDCType(elem.type())) {
+ _current = elem;
+ break;
+ }
+ }
+ }
+
+private:
+ BSONObjIterator _iterator;
+ BSONElement _current;
+};
+
StatusWith<bool> extractMetricsFromDocument(const BSONObj& referenceDoc,
const BSONObj& currentDoc,
std::vector<std::uint64_t>* metrics,
@@ -132,15 +173,14 @@ StatusWith<bool> extractMetricsFromDocument(const BSONObj& referenceDoc,
return {ErrorCodes::BadValue, "Recursion limit reached."};
}
- BSONObjIterator itCurrent(currentDoc);
- BSONObjIterator itReference(referenceDoc);
+ FTDCBSONObjIterator itCurrent(currentDoc);
+ FTDCBSONObjIterator itReference(referenceDoc);
while (itCurrent.more()) {
// Schema mismatch if current document is longer than reference document
if (matches && !itReference.more()) {
LOG(4) << "full-time diagnostic data capture schema change: currrent document is "
- "longer than "
- "reference document";
+ "longer than reference document";
matches = false;
}
@@ -230,6 +270,24 @@ StatusWith<bool> extractMetricsFromDocument(const BSONObj& referenceDoc,
} // namespace
+bool isFTDCType(BSONType type) {
+ switch (type) {
+ case NumberDouble:
+ case NumberInt:
+ case NumberLong:
+ case NumberDecimal:
+ case Bool:
+ case Date:
+ case bsonTimestamp:
+ case Object:
+ case Array:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
StatusWith<bool> extractMetricsFromDocument(const BSONObj& referenceDoc,
const BSONObj& currentDoc,
std::vector<std::uint64_t>* metrics) {
diff --git a/src/mongo/db/ftdc/util.h b/src/mongo/db/ftdc/util.h
index 4816c534f96..04d453139ce 100644
--- a/src/mongo/db/ftdc/util.h
+++ b/src/mongo/db/ftdc/util.h
@@ -168,6 +168,11 @@ StatusWith<BSONObj> getBSONDocumentFromMetadataDoc(const BSONObj& obj);
*/
StatusWith<std::vector<BSONObj>> getMetricsFromMetricDoc(const BSONObj& obj,
FTDCDecompressor* decompressor);
+
+/**
+ * Is this a type that FTDC find's interesting? I.e. is this a numeric or container type?
+ */
+bool isFTDCType(BSONType type);
} // namespace FTDCBSONUtil