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

CrateDB output plugin #3210

Merged
merged 42 commits into from
Nov 9, 2017
Merged

CrateDB output plugin #3210

merged 42 commits into from
Nov 9, 2017

Conversation

felixge
Copy link
Contributor

@felixge felixge commented Sep 8, 2017

Hello,

this PR implements an output plugin for CrateDB using their Postgres Wire Protocol.

Please let me know what kind of changes, if any, you'd like me to make in order to get this merged.

Many thanks in advance 😊
Felix

Required for all PRs:

  • Signed CLA.
  • Associated README.md updated.
  • Has appropriate unit tests.

@danielnelson
Copy link
Contributor

Thanks for the pull request @felixge, I'm pretty far behind on reviews, so it might take me a little while before I am able to review. One thing I notice though is that the plugin uses a different postgres library than the one we use in existing plugins, is it possible to switch to pgx?

@danielnelson danielnelson added the feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin label Sep 8, 2017
@felixge
Copy link
Contributor Author

felixge commented Sep 9, 2017

@danielnelson good idea, I didn't even notice pgx was used already, as I was just looking at other output plugins.

That being said, unfortunately using pgx causes my tests to fail with the following error:

--- FAIL: TestConnectAndWrite (0.02s)
        Error Trace:    cratedb_test.go:30
	Error:      	Received unexpected error:
	            	ERROR: TableUnknownException: Table 'test.pg_type' unknown (SQLSTATE XX000)

This is because pgx queries the postgres catalog when connecting, and CrateDB doesn't implement those catalog tables. As far as I can tell this feature cannot be disabled in pgx.

So if possible, I'd like to keep using lib/pq for this plugin. Let me know what you think.

Cheers
Felix

@felixge felixge mentioned this pull request Sep 9, 2017
@danielnelson
Copy link
Contributor

Thanks for opening the upstream issue. Lets try to get it resolved in pgx so we can avoid increasing the size of the Telegraf binary. In the meantime it makes sense to leave your pull request with pq in case anyone would like to do their own testing.

@felixge
Copy link
Contributor Author

felixge commented Oct 25, 2017

@danielnelson yes, this is related to prepared statements. Prepared statements should always be used when trying to safely inject data into a SQL query. Unfortunately CrateDB doesn't support a certain variant of the Extended Query format (which is used for prepared statements):

Note: A parameter data type can be left unspecified by setting it to zero, or by making the array of parameter type OIDs shorter than the number of parameter symbols ($n) used in the query string.

CrateDB however doesn't determine the number of parameters by looking at the number of placeholders, but instead looks at the length of the array of parameter type OIDs. Pgx (and lib/pq) are not setting this array (as it's optional), causing CrateDB to always complain about too many parameters being given.

Of course one can always try to inject the data by manually escaping it on the client site (which is what sanitize.SanitizeSQL and also my code in this PR) does, but this is always prone to error. But in case of CrateDB, there is no choice.

The only question is: Would you prefer to use the escaping/sanitize mechanism from pgx over the code in this PR? If yes, I'll try to change my code accordingly. But it will most likely require giving up on using the database/sql interface, because the sanitize pkg of pgx is internal, so I can only use it indirectly by using the pgx native APIs ...

Please let me know how you'd like to proceed.

I agree about the object literals. I've added good test coverage for that code to make sure it works.

@danielnelson
Copy link
Contributor

I guess using sanitize is not an option due to being internal, maybe in the future after pgx gains the ability to not use prepared statements we can update it.

@felixge
Copy link
Contributor Author

felixge commented Oct 27, 2017

@danielegozzi yeah. I also think CrateDB will fix the problem preventing prepared statement support on their end at some point. That will be even better.

Anyway, do you think we can move forward with this PR? If yes, are there any remaining improvements you'd like me to make?

@danielnelson danielnelson added this to the 1.5.0 milestone Oct 27, 2017
@danielnelson
Copy link
Contributor

I'll try to do a closer review soon, shouldn't be a problem to finish this for 1.5.

// -> ERROR: SQLParseException: For input string: "14305102049502225714"

cols := []interface{}{
int64(m.HashID()),
Copy link
Contributor

Choose a reason for hiding this comment

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

I hadn't considered the values of this hash to be part stable across versions, would it be a problem if this hash changed even if the series key (measurement name + tagset) did not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Np. I think a cryptographic hash function makes more sense for the use case (deduplication) anyway. Let me know if you think 626f653 works for you.


cols := []interface{}{
int64(m.HashID()),
m.Time().In(loc),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why in local time, what about just sending all times in UTC?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

CrateDB converts to UTC anyway, but you're right, this actually makes things a bit simpler. e246032

{map[string]interface{}{}, `{}`},
{map[string]interface{}(nil), `{}`},
{map[string]interface{}{"foo": "bar"}, `{"foo" = 'bar'}`},
{map[string]interface{}{"foo": "bar", "one": "more"}, `{"foo" = 'bar', "one" = 'more'}`},
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add some extra test cases for string escaping, here are some ideas:

map[string]interface{}{"fo"o": "b'ar", "ab'c": `xy"z`, `on"""e`: "mo'''re"}

Copy link
Contributor Author

@felixge felixge Nov 2, 2017

Choose a reason for hiding this comment

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

I've added the additional test case in c434322 and a new smoke test that validates that the escaped value is a valid SQL literal according to CrateDB: 8b85f0b

Makefile Outdated
@@ -86,6 +86,12 @@ docker-run:
-e SLAPD_CONFIG_ROOTPW="secret" \
-p "389:389" -p "636:636" \
-d cobaugh/openldap-alpine
docker run \--name cratedb \
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to remove the backslash here? \--name

Copy link
Contributor Author

Choose a reason for hiding this comment

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


## Table Schema

The plugin requires a a table with the following schema.
Copy link
Contributor

Choose a reason for hiding this comment

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

Extra a in this sentence

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -0,0 +1,7 @@
version: "3"
Copy link
Contributor

Choose a reason for hiding this comment

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

What do we need the docker compose file for?

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 was using it for local testing. Shouldn't have checked it in. It's gone as of a54054f

@felixge
Copy link
Contributor Author

felixge commented Nov 2, 2017

@danielnelson Thanks for taking the time to review this PR. I've addressed all concerns you raised, please let me know if you'd like me to make any additional changes.

For some reason some circleci tests failed:

--- FAIL: TestWriteHTTPSNoClientAuth (0.01s)
	Error Trace:	http_listener_test.go:233
	Error:      	Received unexpected error:
	            	Post https://localhost:33545/write?db=mydb: x509: certificate has expired or is not yet valid
2017/11/02 19:58:12 I! Started HTTP listener service on :0
2017/11/02 19:58:12 http: TLS handshake error from 127.0.0.1:36421: remote error: tls: bad certificate
2017/11/02 19:58:12 I! Stopped HTTP listener service on  :0
--- FAIL: TestWriteHTTPSWithClientAuth (0.02s)
	Error Trace:	http_listener_test.go:247
	Error:      	Received unexpected error:
	            	Post https://localhost:42092/write?db=mydb: x509: certificate has expired or is not yet valid
...
FAIL
FAIL	github.com/influxdata/telegraf/plugins/inputs/http_listener	6.757s

But I think those are unrelated to my changes.

@felixge
Copy link
Contributor Author

felixge commented Nov 2, 2017

I just merged master into this branch, and there is now an additional test failure:

--- FAIL: TestRateLimiter (0.25s)
	<autogenerated>:1: 
                          
	Error Trace:	limiter_test.go:26
		
	Error:      	Not equal: 
		
	            	expected: 5
		
	            	received: 0
FAIL
FAIL	github.com/influxdata/telegraf/internal/limiter	0.523s

That being said, the latest commit on master is also failing CircleCI right now, so I still think those failures are unrelated to this PR.

@danielnelson
Copy link
Contributor

I fixed the http_listener issue and it should pass now if you rebase.

The test below failed on the previous run, but it seems unrelated to
this PR.

--- FAIL: TestRunTimeout (0.10s)
	<autogenerated>:1: 
                          
	Error Trace:	internal_test.go:57
		
	Error:      	Should be true
FAIL
FAIL	github.com/influxdata/telegraf/internal	0.267s
@felixge
Copy link
Contributor Author

felixge commented Nov 9, 2017

@danielnelson I merged the latest changes from master into this PR and got b0d6723 to pass the circleci checks.

Please let me know if this makes this PR mergeable or if you'd like to see some more improvements.

@danielnelson danielnelson merged commit 62ec3e5 into influxdata:master Nov 9, 2017
@felixge
Copy link
Contributor Author

felixge commented Nov 10, 2017

@danielnelson thank you so much for taking the time to review and merge this PR. 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin new plugin
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants