Skip to content

Commit

Permalink
feat(ffi): Update IR stream protocol version handling in preparation …
Browse files Browse the repository at this point in the history
…for releasing the kv-pair IR stream format: (#573)

- Bump the IR stream protocol version to 0.1.0 for the kv-pair IR stream format.
- Treat the previous IR stream format's versions as backwards compatible.
- Differentiate between backwards-compatible and supported versions during validation.

Co-authored-by: kirkrodrigues <[email protected]>
  • Loading branch information
LinZhihao-723 and kirkrodrigues authored Nov 7, 2024
1 parent 5687ff0 commit 426cc3d
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 54 deletions.
6 changes: 2 additions & 4 deletions components/core/src/clp/ffi/ir_stream/Deserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,8 @@ auto Deserializer<IrUnitHandler>::create(ReaderInterface& reader, IrUnitHandler
return std::errc::protocol_error;
}
auto const version = version_iter->get_ref<nlohmann::json::string_t&>();
// TODO: Just before the KV-pair IR format is formally released, we should replace this
// hard-coded version check with `ffi::ir_stream::validate_protocol_version`.
if (std::string_view{static_cast<char const*>(cProtocol::Metadata::BetaVersionValue)}
!= version)
if (ffi::ir_stream::IRProtocolErrorCode::Supported
!= ffi::ir_stream::validate_protocol_version(version))
{
return std::errc::protocol_not_supported;
}
Expand Down
2 changes: 1 addition & 1 deletion components/core/src/clp/ffi/ir_stream/Serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ auto Serializer<encoded_variable_t>::create(
ir_buf.insert(ir_buf.cend(), cMagicNumber.begin(), cMagicNumber.end());

nlohmann::json metadata;
metadata.emplace(cProtocol::Metadata::VersionKey, cProtocol::Metadata::BetaVersionValue);
metadata.emplace(cProtocol::Metadata::VersionKey, cProtocol::Metadata::VersionValue);
metadata.emplace(cProtocol::Metadata::VariablesSchemaIdKey, cVariablesSchemaVersion);
metadata.emplace(
cProtocol::Metadata::VariableEncodingMethodsIdKey,
Expand Down
45 changes: 28 additions & 17 deletions components/core/src/clp/ffi/ir_stream/decoding_methods.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include "decoding_methods.hpp"

#include <algorithm>
#include <array>
#include <regex>
#include <string>
#include <string_view>

#include "../../ir/types.hpp"
#include "byteswap.hpp"
Expand Down Expand Up @@ -468,33 +472,40 @@ IRErrorCode deserialize_preamble(
return IRErrorCode_Success;
}

IRProtocolErrorCode validate_protocol_version(std::string_view protocol_version) {
if ("v0.0.0" == protocol_version) {
// This version is hardcoded to support the oldest IR protocol version. When this version is
// no longer supported, this branch should be removed.
return IRProtocolErrorCode_Supported;
auto validate_protocol_version(std::string_view protocol_version) -> IRProtocolErrorCode {
// These versions are hardcoded to support the IR protocol version that predates the key-value
// pair IR format.
constexpr std::array<std::string_view, 3> cBackwardCompatibleVersions{
"v0.0.0",
"0.0.1",
cProtocol::Metadata::LatestBackwardCompatibleVersion
};
if (cBackwardCompatibleVersions.cend()
!= std::ranges::find(cBackwardCompatibleVersions, protocol_version))
{
return IRProtocolErrorCode::BackwardCompatible;
}
std::regex const protocol_version_regex{cProtocol::Metadata::VersionRegex};

std::regex const protocol_version_regex{
static_cast<char const*>(cProtocol::Metadata::VersionRegex)
};
if (false
== std::regex_match(
protocol_version.begin(),
protocol_version.end(),
protocol_version_regex
))
{
return IRProtocolErrorCode_Invalid;
return IRProtocolErrorCode::Invalid;
}
std::string_view current_build_protocol_version{cProtocol::Metadata::VersionValue};
auto get_major_version{[](std::string_view version) {
return version.substr(0, version.find('.'));
}};
if (current_build_protocol_version < protocol_version) {
return IRProtocolErrorCode_Too_New;
}
if (get_major_version(current_build_protocol_version) > get_major_version(protocol_version)) {
return IRProtocolErrorCode_Too_Old;

// TODO: Currently, we hardcode the supported versions. This should be removed once we
// implement a proper version parser.
if (cProtocol::Metadata::VersionValue == protocol_version) {
return IRProtocolErrorCode::Supported;
}
return IRProtocolErrorCode_Supported;

return IRProtocolErrorCode::Unsupported;
}

IRErrorCode deserialize_utc_offset_change(ReaderInterface& reader, UtcOffset& utc_offset) {
Expand Down
31 changes: 18 additions & 13 deletions components/core/src/clp/ffi/ir_stream/decoding_methods.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CLP_FFI_IR_STREAM_DECODING_METHODS_HPP
#define CLP_FFI_IR_STREAM_DECODING_METHODS_HPP

#include <cstdint>
#include <string>
#include <vector>

Expand All @@ -20,12 +21,12 @@ typedef enum {
IRErrorCode_Incomplete_IR,
} IRErrorCode;

typedef enum {
IRProtocolErrorCode_Supported,
IRProtocolErrorCode_Too_Old,
IRProtocolErrorCode_Too_New,
IRProtocolErrorCode_Invalid,
} IRProtocolErrorCode;
enum class IRProtocolErrorCode : uint8_t {
Supported,
BackwardCompatible,
Unsupported,
Invalid,
};

class DecodingException : public TraceableException {
public:
Expand Down Expand Up @@ -193,15 +194,19 @@ IRErrorCode deserialize_utc_offset_change(ReaderInterface& reader, UtcOffset& ut
/**
* Validates whether the given protocol version can be supported by the current build.
* @param protocol_version
* @return IRProtocolErrorCode_Supported if the protocol version is supported.
* @return IRProtocolErrorCode_Too_Old if the protocol version is no longer supported by this
* build's protocol version.
* @return IRProtocolErrorCode_Too_New if the protocol version is newer than this build's protocol
* version.
* @return IRProtocolErrorCode_Invalid if the protocol version does not follow the SemVer
* @return IRProtocolErrorCode::Supported if the protocol version is supported by the key-value
* pair IR stream serializer and deserializer. TODO: Update this once we integrate backwards
* compatibility into the deserializer.
* @return IRProtocolErrorCode::BackwardCompatible if the protocol version is supported by the
* serializer and deserializer for the IR stream format that predates the key-value pair IR stream
* format. TODO: Update this once we integrate backwards compatibility into the key-value pair IR
* stream format.
* @return IRProtocolErrorCode::Unsupported if the protocol version is not supported by this build.
* @return IRProtocolErrorCode::Invalid if the protocol version does not follow the SemVer
* specification.
*/
IRProtocolErrorCode validate_protocol_version(std::string_view protocol_version);
[[nodiscard]] auto validate_protocol_version(std::string_view protocol_version
) -> IRProtocolErrorCode;

namespace eight_byte_encoding {
/**
Expand Down
3 changes: 2 additions & 1 deletion components/core/src/clp/ffi/ir_stream/encoding_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ static void add_base_metadata_fields(
string_view time_zone_id,
nlohmann::json& metadata
) {
metadata[cProtocol::Metadata::VersionKey] = cProtocol::Metadata::VersionValue;
metadata[cProtocol::Metadata::VersionKey]
= cProtocol::Metadata::LatestBackwardCompatibleVersion;
metadata[cProtocol::Metadata::VariablesSchemaIdKey] = cVariablesSchemaVersion;
metadata[cProtocol::Metadata::VariableEncodingMethodsIdKey] = cVariableEncodingMethodsVersion;
metadata[cProtocol::Metadata::TimestampPatternKey] = timestamp_pattern;
Expand Down
7 changes: 5 additions & 2 deletions components/core/src/clp/ffi/ir_stream/protocol_constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <cstddef>
#include <cstdint>
#include <string_view>
#include <type_traits>

namespace clp::ffi::ir_stream::cProtocol {
Expand All @@ -12,8 +13,10 @@ constexpr int8_t LengthUByte = 0x11;
constexpr int8_t LengthUShort = 0x12;

constexpr char VersionKey[] = "VERSION";
constexpr char VersionValue[] = "0.0.2";
constexpr char BetaVersionValue[] = "0.1.0-beta.1";
constexpr std::string_view VersionValue{"0.1.0"};

// This is used for the IR stream format that predates the key-value pair IR format.
constexpr std::string_view LatestBackwardCompatibleVersion{"0.0.2"};

// The following regex can be used to validate a Semantic Versioning string. The source of the
// regex can be found here: https://semver.org/
Expand Down
2 changes: 1 addition & 1 deletion components/core/src/clp/ir/LogEventDeserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ auto LogEventDeserializer<encoded_variable_t>::create(ReaderInterface& reader
return std::errc::protocol_error;
}
auto metadata_version = version_iter->get_ref<nlohmann::json::string_t&>();
if (ffi::ir_stream::IRProtocolErrorCode_Supported
if (ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible
!= ffi::ir_stream::validate_protocol_version(metadata_version))
{
return std::errc::protocol_not_supported;
Expand Down
76 changes: 61 additions & 15 deletions components/core/tests/test-ir_encoding_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,8 +630,8 @@ TEMPLATE_TEST_CASE(
auto metadata_json = nlohmann::json::parse(json_metadata);
std::string const version
= metadata_json.at(clp::ffi::ir_stream::cProtocol::Metadata::VersionKey);
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported == validate_protocol_version(version)
);
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible
== validate_protocol_version(version));
REQUIRE(clp::ffi::ir_stream::cProtocol::Metadata::EncodingJson == metadata_type);
set_timestamp_info(metadata_json, ts_info);
REQUIRE(timestamp_pattern_syntax == ts_info.timestamp_pattern_syntax);
Expand Down Expand Up @@ -844,17 +844,63 @@ TEST_CASE("decode_next_message_four_byte_timestamp_delta", "[ffi][deserialize_lo
}

TEST_CASE("validate_protocol_version", "[ffi][validate_version_protocol]") {
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Invalid == validate_protocol_version("v0.0.1")
REQUIRE(
(clp::ffi::ir_stream::IRProtocolErrorCode::Supported
== validate_protocol_version(clp::ffi::ir_stream::cProtocol::Metadata::VersionValue))
);
REQUIRE(
(clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible
== validate_protocol_version(
clp::ffi::ir_stream::cProtocol::Metadata::LatestBackwardCompatibleVersion
))
);
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Invalid == validate_protocol_version("0.1"));
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Invalid == validate_protocol_version("0.a.1"));

REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Too_New
== validate_protocol_version("1000.0.0"));
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported
== validate_protocol_version(clp::ffi::ir_stream::cProtocol::Metadata::VersionValue));
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported
== validate_protocol_version("v0.0.0"));

SECTION("Test invalid versions") {
auto const invalid_versions{GENERATE(
std::string_view{"v0.0.1"},
std::string_view{"0.1"},
std::string_view{"0.1.a"},
std::string_view{"0.a.1"}
)};
REQUIRE(
(clp::ffi::ir_stream::IRProtocolErrorCode::Invalid
== validate_protocol_version(invalid_versions))
);
}

SECTION("Test backward compatible versions") {
auto const backward_compatible_versions{GENERATE(
std::string_view{"v0.0.0"},
std::string_view{"0.0.1"},
std::string_view{"0.0.2"}
)};
REQUIRE(
(clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible
== validate_protocol_version(backward_compatible_versions))
);
}

SECTION("Test versions that're too old") {
auto const old_versions{GENERATE(
std::string_view{"0.0.3"},
std::string_view{"0.0.3-beta.1"},
std::string_view{"0.1.0-beta"}
)};
REQUIRE(
(clp::ffi::ir_stream::IRProtocolErrorCode::Unsupported
== validate_protocol_version(old_versions))
);
}

SECTION("Test versions that're too new") {
auto const new_versions{
GENERATE(std::string_view{"10000.0.0"}, std::string_view{"0.10000.0"})
};
REQUIRE(
(clp::ffi::ir_stream::IRProtocolErrorCode::Unsupported
== validate_protocol_version(new_versions))
);
}
}

TEMPLATE_TEST_CASE(
Expand Down Expand Up @@ -905,8 +951,8 @@ TEMPLATE_TEST_CASE(
string_view json_metadata{json_metadata_ptr, metadata_size};
auto metadata_json = nlohmann::json::parse(json_metadata);
string const version = metadata_json.at(clp::ffi::ir_stream::cProtocol::Metadata::VersionKey);
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported == validate_protocol_version(version)
);
REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible
== validate_protocol_version(version));
REQUIRE(clp::ffi::ir_stream::cProtocol::Metadata::EncodingJson == metadata_type);
set_timestamp_info(metadata_json, ts_info);
REQUIRE(timestamp_pattern_syntax == ts_info.timestamp_pattern_syntax);
Expand Down Expand Up @@ -1055,7 +1101,7 @@ TEMPLATE_TEST_CASE(
nlohmann::json expected_metadata;
expected_metadata.emplace(
clp::ffi::ir_stream::cProtocol::Metadata::VersionKey,
clp::ffi::ir_stream::cProtocol::Metadata::BetaVersionValue
clp::ffi::ir_stream::cProtocol::Metadata::VersionValue
);
expected_metadata.emplace(
clp::ffi::ir_stream::cProtocol::Metadata::VariablesSchemaIdKey,
Expand Down

0 comments on commit 426cc3d

Please sign in to comment.