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

tracetest fails to compile in C++ #35

Closed
isaachier opened this issue Oct 15, 2017 · 31 comments
Closed

tracetest fails to compile in C++ #35

isaachier opened this issue Oct 15, 2017 · 31 comments

Comments

@isaachier
Copy link
Contributor

tracetest.thrift declares a recursive member in its struct. I have found the generated code fails to compile in C++. It seems Thrift does not support any sort of recursive types as far as I can tell. What is the best way to fix/address this?

@yurishkuro
Copy link
Member

I haven't used C++ for a while, but isn't there a way to pre-declare the type so that you can declare a pointer to it? what does the code look like?

@isaachier
Copy link
Contributor Author

I saw something called cpp_type for Thrift which could help here. Usually the solution would be to use a pointer to the type instead of the actual type. Thrift doesn't seem to directly allow that.

Example:

struct Downstream {
  //...
  Downstream* downstream;
};

@isaachier
Copy link
Contributor Author

Actually, looking at the file it is clear to me that Downstream can probably be wrapped in a list without the last member. Not sure how to do this without breaking backwards compatibility with other clients.

@isaachier
Copy link
Contributor Author

Original issue in Apache Thrift: apache/thrift#84.

@isaachier
Copy link
Contributor Author

isaachier commented Oct 15, 2017

One last thought. The reason this only fails in C++ and not any other language relates to the way optional is implemented in C++. Unlike Go, optional does not generate a pointer member in C++. Instead, the generated code looks something like this:

Thrift:

struct Foo {
    2: optional Bar bar
}

C++:

typedef struct _Foo__isset {
    bool bar :1;
} _Foo_isset;

class Foo {
    Bar bar;
    _Foo__isset __isset;

    void __set_bar(const Bar& b) { bar = b; __isset.bar = true; }
};

@isaachier
Copy link
Contributor Author

@yurishkuro this might block C++ client crossdock testing if it cannot be changed.

@yurishkuro
Copy link
Member

can we fudge the generated files? Changing tracetest API is not impossible, but rather involved, we'd need to do it across all clients, unless the IDL change can be made compatible with the existing format.

@isaachier
Copy link
Contributor Author

Ya that is fine with me. Might be worth noting as a "TODO." Newer versions of Thrift fix this, but not 0.9.2.

@yurishkuro
Copy link
Member

why do you need 0.9.2? I believe you can use 0.9.3 or 0.10, they are compatible in the actual payloads, just the language APIs are different, which is not a problem for you.

@isaachier
Copy link
Contributor Author

isaachier commented Oct 16, 2017 via email

@yurishkuro
Copy link
Member

IIRC we only use 0.9.2 in Java, for legacy reasons. Go uses 0.9.3, JS/Python don't use Thrift at all (they use thriftrw).

@isaachier
Copy link
Contributor Author

isaachier commented Oct 16, 2017 via email

@yurishkuro
Copy link
Member

is there a way to shadow/vendor thrift code in C++ so that it doesn't conflict with people's dependencies?

@isaachier
Copy link
Contributor Author

isaachier commented Oct 16, 2017 via email

@isaachier
Copy link
Contributor Author

I'm wondering how this is done in any compiled language. When linking together an executable, the linker doesn't care where the symbols come from, as long as they are resolved. Even if I make a library that included the Thrift library sources, wouldn't the linker ultimately discard the symbols that it doesn't need?

Example:
g++ -o myapp -L/path/to/thrift-0.9.2 -ljaegertracing -L/path/to/jaeger -ljaegertracing -L/path/to/thrift-0.10.0

The above case will probably drop the symbols in Thrift 0.10.0 in favor of the existing symbols in Thrift 0.9.2.

@yurishkuro
Copy link
Member

In Java this is achieved via "shading" which rewrites the package name of the included module, e.g. instead of com.guava you would have io.jaeger.shade.com.guava in the final JAR.

In Go I've seen people simply copying the vendored code into a sub-dir, which also results in a separate package name.

Of course this approach only works if the shaded packages are not exposed via your module's public API.

@isaachier
Copy link
Contributor Author

OK I see. Java and Go both actually define symbols with the path as a prefix/namespace. In C++ this all has to be done by hand. Unfortunately, libraries rarely do anything like that. An example of how that could be accomplished.

namespace v2 {
namespace apache {
namespace thrift {
// ...
}
}
}

More often they are actually included in a separate directory and linked with a different library.

@isaachier
Copy link
Contributor Author

Still a problem, but fixed temporarily with this: isaachier/jaeger-client-cpp@10719ae.

@isaachier
Copy link
Contributor Author

Just going to resolve this for now.

@ringerc
Copy link

ringerc commented Jan 31, 2018

This is different to the Thrift pull req in apache/thrift#84, since that didn't address optional self-membership.

I've opened a new Thrift bug https://issues.apache.org/jira/browse/THRIFT-4484 .

This blocks code-generation for the c++ library. It has to apply a manual patch to the generated code right now, making it hard to use other Thrift versions, automate builds more, etc.

What would the consequences of changing how this is modeled in the IDL be?

@yurishkuro
Copy link
Member

tracetest defines a schema for cross-language integration tests of the instrumentation libraries. Not impossible to change the schema, but the usual caveats of upgrading across 5 projects

@ringerc
Copy link

ringerc commented Jan 31, 2018

@yurishkuro OK, good to know. Unfortunately it looks like zipkincore.thrift is affected too; unsure if working around that with an IDL change introduces a compatibility issue with zipkin.

Ideally Thrift would just generate sane C++...

ringerc added a commit to ringerc/jaeger-idl that referenced this issue Feb 1, 2018
This IDL is fine for languages where everything is by-reference. But for C++, it tries to embed a class by-value into its self, which cannot succeed, and results in compilation failure. See jaegertracing#35

This has been worked around by patching the IDL-generated code; see jaegertracing/jaeger-client-cpp#45 .

It turns out we can just tell Thrift to include the optional member by-reference. See the comment in https://issues.apache.org/jira/browse/THRIFT-4484  about `cpp.ref`. Apparently that's now spelled `&` in Thrift IDL. I don't know what effect it has for other languages, the documentation is sparse at best, and doesn't mention this at all.

If this doesn't break other languages I think it's the correct fix for the IDL.
@ringerc
Copy link

ringerc commented Feb 1, 2018

The fix to the IDL is trivial. I don't know Thrift well enough (or at all) so can't say if it will affect the wire format, but I'm guessing not?

More of a concern is whether it affects the other bindings' generated code. See ringerc#1 .

@isaachier
Copy link
Contributor Author

@yurishkuro reminded me a while back that the Thrift wire protocol is unlikely to change at all between minor versions of Thrift. The only changes will be the headers used etc. I only use 0.9.2 for an internal Uber project. Otherwise, if a later version supports recursive types, feel free to use that version instead. As I have pointed out before, please ignore Zipkin entirely if possible. I understand that there is a method using a Thrift type as an argument, but the argument is unused and should not be needed for compilation.

@ringerc
Copy link

ringerc commented Feb 1, 2018 via email

@ringerc
Copy link

ringerc commented Feb 2, 2018

It looks like the problem IDL is confined to use by Crossdock, not the main protocol and project.

So Jaeger should probably just patch it to something Thrift doesn't choke on.

ringerc added a commit to ringerc/jaeger-cpp-client that referenced this issue Feb 16, 2018
We need a newer Thrift to support hosts with openssl 1.1. Since Jaeger its
self requires a c++11 compiler and nlohmann-json requires at least g++ 4.9,
supporting newer dependencies makes sense. But Thrift 0.9.2 didn't support
openssl 1.1.

This is some pretty brutal surgery:

- Remove crossdock and zipkin support entirely. The IDL is broken for C++ /
  Thrift generates bad code for it. See
  jaegertracing/jaeger-idl#35,
  https://issues.apache.org/jira/browse/THRIFT-4484
  jaegertracing#45

- Adapt Thrift's namespace-aliasing approach to support Thrift built with
  use of std::smart_ptr or boost::smart_ptr; Thrift switched to std::smart_ptr
  in later versions but retains boost::smart_ptr for BC.

- Regenerate IDL with Thrift 0.11.0 and commit in-tree.

Unsure if Hunter still works; may need to package Thrift 0.11.0

Haven't tested with a Thrift configured with Boost smart pointers. Don't do
that.

The wholesale removal of zipkin and crossdock support means this is unlikely to
be committable to mainline. Hopefully Thrift will fix their codegen. If not,
well, we're probably going to throw Thrift out entirely soon.
@ringerc
Copy link

ringerc commented Feb 16, 2018

This is a blocker for making the cpp-client work on a Thrift other than 0.9.2. Since Thrift 0.9.2 doesn't support openssl 1.1, it's a pretty major source of pain.

The bad IDL only appears to affect Zipkin support and Crossdock support, so the impact of fixing it even if it makes a wire protocol change will only affect users of those features.

Please?

In the mean time I've ripped out Zipkin and Crossdock support from jaeger cpp-client so I can build it on Thrift 0.11.0 and OpenSSL 1.1; see jaegertracing/jaeger-client-cpp#61 for the working branch. This is not mergeable.

@ringerc
Copy link

ringerc commented Feb 16, 2018

Ping @yurishkuro for practicality of removing the faulty optional self-referential usage from the IDL.

@isaachier
Copy link
Contributor Author

I don't see the need to change the type. If anything, I heard more recent versions of Thrift support the recursive references.

@ringerc
Copy link

ringerc commented Feb 16, 2018 via email

@ringerc
Copy link

ringerc commented Aug 23, 2018

When I tested Thrift 0.11.0 it produced differently wrong results. I'll check. Yeah, it does, per your comments on #94.

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

No branches or pull requests

3 participants