Skip to content
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

request_info: initial implementation of dynamic metadata object #3918

Merged
merged 33 commits into from
Aug 5, 2018

Conversation

curiouserrandy
Copy link
Contributor

Description: Add functionality for setting and getting arbitrarily typed information for a request by filters associated with that request. This is the initial PR to implement https://docs.google.com/document/d/1GNawM59Pp09WZ34zqJYiO_qmERjuGbOUeAqF1GG6v9Q/edit. Following this PR I intend to switch all current uses of dynamic metadata over to this method, then implement the %DYNAMIC_METADATA% substitution template for user defined headers.

Risk Level: Low (no functionality change in this PR).

Testing: Unit tests.

Docs Changes: None.

Release Notes: None.

Fixes #151

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Copy link
Member

@zuercher zuercher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on. I think the overall structure makes sense, but I have some questions on the details.

EXPECT_THROW(
dynamic_metadata().setData("test_1", std::make_unique<TestStoredType>(2, nullptr, nullptr)),
EnvoyException);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests for getData with an unknown name and getData with the wrong type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catches; thanks. Done.

virtual ~DynamicMetadata(){};

// May only be called once for a particular |data_name|
template <typename T> void setData(absl::string_view data_name, std::unique_ptr<T>&& data) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add doxygen-style comments for these methods per the style guide. This method in particular should probably get a note that setData with a duplicate name will destroy data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (sorry to have missed this).

In terms of a duplicate name destroying data: If you ask me again I'll do it, but to my mind calling it with a duplicate name is an interface violation, so it feels a little funny to me to say "Don't do this. Oh, and if you do, the owning pointer you passed in will be destructed, resulting in deallocation of the data you passed."

(It occurs to me that this interface is incomplete, in that it's not possible for a consumer that doesn't have prior knowledge to be sure it isn't going to do something that be an interface contract violation. I've added a hasData<> method to solve that problem. Let me know if you want other changes/reversions in this space.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine. I suppose the pointer has to be create inline with the call or else moved, so it shouldn't be surprising that it's freed on error.

* More general dynamic metadata object.
* TODO(rdsmith): Replace uses of above with this object.
*/
virtual DynamicMetadata& dynamicMetadata2() PURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps leave this out for now and replace the appropriate methods when you're ready to swap them in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

namespace Envoy {
namespace RequestInfo {

class StringAccessor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class should get doxygen comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

namespace Envoy {
namespace RequestInfo {

class StringAccessorImpl : public StringAccessor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you expect this to be used? I would imagine that dynamic metadata types would normally just extend StringAccessor and provide an asString() based on some internal data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, and if you'd like I'm happy to nuke it--it certainly isn't going to cause anyone grief to create the equivalent. My thought was that for many such situations, the source of the data might not want to deal with the ownership/lifetime issues involved in owning the string data (or whatever is used to create the string data) and hence might simple want to hand over to DynamicMetadata an accessor that can be used for user-defined headers and forget about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My inclination would be to leave it out, but let's see what other people think.

template <typename T> class Traits {
public:
static size_t getTypeId() {
static const size_t type_id = ++type_id_index_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this access to type_id_index_ is thread safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, sorry, I haven't yet gotten re-used to doing multi-threaded programming. Done.

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
@curiouserrandy
Copy link
Contributor Author

Thanks for the review! I think it's ready for another round (only trivial changes since all auto-tests passed on my last push), but feel free to wait until the auto-tests pass on this commit before diving back in.

@curiouserrandy
Copy link
Contributor Author

Actually, my last changes included a merge to ToT, so maybe it's worth waiting :-}.

@curiouserrandy
Copy link
Contributor Author

Ready for next round of review.

@zuercher
Copy link
Member

@htuch and/or @mattklein123 ptal

@rshriram
Copy link
Member

is requestInfo specific to HTTP? I remember seeing folks pushing it up so that TCP filters could log similar info (for access logs, etc.). If so, then can this be used as a way to share info across filters in TCP stack as well?

@curiouserrandy
Copy link
Contributor Author

For anyone following along: I just responded to this question on the doc @ https://docs.google.com/document/d/1GNawM59Pp09WZ34zqJYiO_qmERjuGbOUeAqF1GG6v9Q/edit?disco=AAAACB6UHU4. TL;DR: No basic reason it couldn't, though stream multiplexing needs to be considered, but I'm not currently planning to do that work.

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
@curiouserrandy
Copy link
Contributor Author

The newly added probe functions are to allow for better error handling when implementing user-defined headers using this mechanism. I also added some doxygen comments and const markers on methods.

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this looks really clean, nice!

hdrs = ["dynamic_metadata.h"],
external_deps = ["abseil_optional"],
deps = [
],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: No need for empty deps here and below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

namespace Envoy {
namespace RequestInfo {

std::atomic<size_t> DynamicMetadata::type_id_index_(0u);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm a bit skeptical about state living in include/envoy. But, I don't see a good alternative, since I gather that other parts of the interface depend on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on both counts. Happy to brainstorm with you on this--I couldn't come up with a different way to do it with the templated interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other alternative is to put it in source/common/request_info. There is precedent here; we have various deps on assert.h, protobuf.h in source/common/ already in include/envoy, but no .cc. I think it would be least bad to do this than add a .cc to include/envoy. But, this is bike shedding a bit. @mattklein123 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion. I guess what @htuch proposes is more inline w/ what we already have. I might drop a comment in here explaining why this is the way it is if we keep it this way since it's so unlike anything else we have.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no feelings at all about how to implement this, so I moved the .cc file over to source/common/request_info and re-jiggered the dependencies. I do want a review on that move before landing, LGTM or no :-}.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to push? I don't see any changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, sorry, still working on other changes so haven't pushed yet. We got into a bit of a bikeshed internally around one of Harvey's other comments.

Tangent question on github comment use: I usually respond to these comments as I make changes in my local checkout, then flag that I'm ready for a new review after I push and the auto tests pass. Is there a better pattern that would confuse reviewers less? Or is it just that there was a long (not yet complete) gap between comment and push?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no defined pattern, I just noticed that you said "I do want a review" and there was nothing to review. :) No problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This request is now moot--the re-implementation requested by Harvey elsewhere means that we no longer need the typeid jiggery.

/**
* @param data_name the name of the data being set.
* @param data an owning pointer to the data to be stored.
* Note that it is an error to call setData() twice with the same data_name.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this limitation going to be an issue anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume you mean in the currently existing uses of dynamic metadata?

The simple answer is "I don't know--haven't really started on the conversion yet". The more complex answer is:

  • This was put in as a way to keep the use of dynamic metadata under control, specifically, to make sure without dependency management (since that implementation comes later) that only one "source" sets any particular piece of data.
  • The structure that allows for lazy evaluation also allows for the dynamic metadata source to change DM entries without that needing to go through the DM interface. So if there are single sources that want to change DM over the lifetime of the request, that can still be implemented within this restriction. The only thing this really forbids is something setting a piece of data and then something else overriding it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed offline, I expanded the comment here to give more of an explanation for this choice. I looked at STYLE.md to see if adding something there made sense, but it didn't seem to me that it would. I scanned the names of the other .md files in that directory and didn't see an obvious place to put something like this. Willing to take a look and see if there's some specific place you think I should put more information?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, as long as this is covered in the expanded comment, all good.

}

/**
* @param data_name the name of the data being set.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method doesn't set..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Done.

}

/**
* @param data_name the name of the data being set.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method doesn't set..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* Contains a string in a form which is usable with DynamicMetadata and
* allows lazy evaluation if needed.
*/
class StringAccessor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I fully grok how this is used from the comments here and the rest of the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the basic idea here is that "StringAccessor" is how user defined headers (working on the PR now) get access to DynamicMetadata. That may mean I should pull it into that PR, and (?) move its definition over to include/envoy/router. Or maybe I should just expand the comment here to say "Intended to be a globally known interface for accessing string data on DynamicMetadata".

What's your preference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, pushing to next PR.

void (*destructor)(void*)) {
if (data_storage_.find(data_name) != data_storage_.end()) {
(*destructor)(data);
throw EnvoyException("DynamicMetadata::setData<T> called twice with same name.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be an ASSERT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My inclination is against, but there may be issues I'm not thinking about.

My argument would be that this can occur because of a configuration error (imagine two filters that are variants of each other that set the same output data, and someone configures them in the same filter chain), and I believe that throws result in cleanly handled configuration errors where asserts always crash. But happy to take your guidance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that is a valid use case.

throw EnvoyException("DynamicMetadata::setData<T> called twice with same name.");
}

data_storage_[static_cast<std::string>(data_name)] = {type_id, data, destructor};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can static cast string view to string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. I'm not sure it's the clearest thing to me as a reader, but I don't objective enough to want to ask you to change it :)

it.second.destructor_(it.second.ptr_);
}
}
data_storage_.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't the implicit destructor do this for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is just my discomfort with leaving pointers into freed memory around for even a little bit of time after they're freed. But agreed, there's really no point. Removed.

throw EnvoyException("DynamicMetadata::setData<T> called twice with same name.");
}

data_storage_[static_cast<std::string>(data_name)] = {type_id, data, destructor};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not combine data name + type ID in the key, so that different types have independent namespaces?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm inclined to believe that, philosophically, having a single text based namespace is the right thing. The concrete example is that if someone tries to access data in a UDH that they set in a filter but didn't use StringAccessor, they'll get a "wrong type" error rather than a "data not found" error. And I'm having a hard time coming up with an example where I'd want two pieces of data with different types to have the same reverse DNS name; when would that make sense? Maybe if they were two different ways of saying the same thing? But in that case, why not have a single class with two different accessors? (Other than UDH, which needs StringAccessor.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed in person, I agree; we're essentially getting dynamic type checks here, which is a good thing.

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
…icMetadata

Signed-off-by: Randy Smith <rdsmith@google.com>
@curiouserrandy
Copy link
Contributor Author

Harvey, PTAL? I think this is ready for another round of review.

I'm getting test timeouts occasionally on tsan (different ones running locally than in CI); let me know what I should be doing about that.

@htuch
Copy link
Member

htuch commented Jul 31, 2018

TSAN timeouts are in @envoy//test/extensions/filters/network/thrift_proxy:filter_integration_test, so unrelated to your PR. I've kicked CI. CC @alyssawilk (Envoy maintainer on-call, integration test deflake Jedi) and @zuercher (Thrift extension owner).

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo remaining comments, this is great.

namespace Envoy {
namespace RequestInfo {

std::atomic<size_t> DynamicMetadata::type_id_index_(0u);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other alternative is to put it in source/common/request_info. There is precedent here; we have various deps on assert.h, protobuf.h in source/common/ already in include/envoy, but no .cc. I think it would be least bad to do this than add a .cc to include/envoy. But, this is bike shedding a bit. @mattklein123 ?

* Note that it is an error to access data that has not previously been set.
*/
template <typename T> const T& getData(absl::string_view data_name) const {
return *static_cast<T*>(getDataGeneric(data_name, Traits<T>::getTypeId()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider adding an abstract empty class DynamicMetadataObject that all eligible metadata object inherit from? That way, you could us a safer dynamic_cast here to catch type violations via RTTI. This is what is done for TLS, see https://github.com/envoyproxy/envoy/blob/master/include/envoy/thread_local/thread_local.h#L16 and its uses (https://github.com/envoyproxy/envoy/blob/master/include/envoy/thread_local/thread_local.h#L41).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. That's close to a complete rewrite, so may require a fuller re-review. It also resulted in changes to the design doc (https://docs.google.com/document/d/1GNawM59Pp09WZ34zqJYiO_qmERjuGbOUeAqF1GG6v9Q/edit?pli=1); the TL;DR is that setData now just takes a pointer to the abstract empty class and that since dynamic_cast<> is used for retrieval, any type that the actual type of the stored data can be safely cast to will work.

It also made some other changes obsolete; I'll try to mark those individually.

}

bool DynamicMetadataImpl::hasDataWithName(absl::string_view data_name) const {
return (data_storage_.find(data_name) != data_storage_.end());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: return data_storage_.count(data_name) > 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

bool DynamicMetadataImpl::hasDataGeneric(absl::string_view data_name, size_t type_id) const {
const auto& it = data_storage_.find(data_name);

return (it != data_storage_.end() && it->second.typeid_ == type_id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIt: parens not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moot, but done in the other method.

template <typename T> class Traits {
public:
static size_t getTypeId() {
static const size_t type_id = type_id_index_.fetch_add(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a shame we can't just get at RTTI for this BTW, but I'm guessing you've gone down that path..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope; all my recent work has been in codebases that avoided RTTI like the plague. But the interface shift you suggest doesn't do us any good without RTTI, and agreed that the C++ internal implementation is likely to be better than mine :-}, so done.

void (*destructor_)(void*);
};

std::map<std::string, Data, std::less<>> data_storage_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: is there a reason this is ordered? Since we're not traversing, I don't know if there is a strong case here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This turns out to be necessary in order to use an absl::string_view as a find() argument. See https://en.cppreference.com/w/cpp/container/map/find, specifically instances 3 & 4 of the method.

}
}

int Access() const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/Access/access/ for Envoy style

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops. Done.

}
}

int Access() const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this make sense as const given it mutates stuff, or is access count hidden and so it's fine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this strikes me as the bitwise const versus conceptually const argument, on which I've always landed on the conceptual side. Also note that the function must be const, as dynamic_metadata() always returns a const reference for stored functions. That + this being in a test file makes me think that this is fine. Let me know if you disagree.

public:
DynamicMetadataImplTest() { ResetDynamicMetadata(); }

void ResetDynamicMetadata() { dynamic_metadata_ = std::make_unique<DynamicMetadataImpl>(); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: resetDynamicMetadata.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
…icMetadata

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woah, way simpler. I'm good with this one over the previous version, seems easier to grok, thanks for taking the time to iterate on this.


class DynamicMetadata {
public:
class DynamicMetadataObject {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: If this is going to be nested in DynamicMetadata, you can just call it Object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

const auto& it = data_storage_.find(data_name);

if (it == data_storage_.end()) {
throw EnvoyException("DynamicMetadata::getData<T> called for unknown data name.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: getDataGeneric

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intentionally made it getData<> as the receiver of the throw will be above the public API of the DynamicMetadata class, and hence will be more interested in what they did wrong in targeting that public API. Given that, do you still want it spelled getDataGeneric?

class DynamicMetadataImpl : public DynamicMetadata {
public:
DynamicMetadataImpl() {}
~DynamicMetadataImpl() override{};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need either of these empty ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

size_t access_count_1 = 0u;
size_t access_count_2 = 0u;
size_t destruction_count = 0u;
const int ValueOne = 5;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: static const

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
Signed-off-by: Randy Smith <rdsmith@google.com>
@curiouserrandy
Copy link
Contributor Author

Ok, ready for another round of review. (The test failure on the last round was a timeout that didn't seem to be related to my changes.)

htuch
htuch previously approved these changes Aug 2, 2018
Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rad, LGTM; @zuercher @mattklein123 ?

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Few small comments.

const Object* getDataGeneric(absl::string_view data_name) const override;

private:
std::map<std::string, std::unique_ptr<Object>, std::less<>> data_storage_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: What's the reason for specifying std::less here? Comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment.

if (data_storage_.find(data_name) != data_storage_.end()) {
throw EnvoyException("DynamicMetadata::setData<T> called twice with same name.");
}
data_storage_[static_cast<std::string>(data_name)] = std::move(data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: using a cast here is a bit strange to me. Is this just to force type coercion when it doesn't happen otherwise?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; absl::string_view doesn't convert to std::string unless you ask it nicely :-}. Added a comment.


TEST_F(DynamicMetadataImplTest, NameConflict) {
dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(1));
EXPECT_THROW(dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(2)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do EXPECT_THROW_WITH_MESSAGE for here and below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Signed-off-by: Randy Smith <rdsmith@google.com>
@curiouserrandy
Copy link
Contributor Author

Ready for next round of review (changes were minor enough that I'm assuming any problems would have been caught with bazel test //test/..., which I did on my workstation).

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants