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

cockroachdb: add high-availability support #12965

Merged
merged 1 commit into from
Mar 29, 2022

Conversation

DuskEagle
Copy link
Contributor

This commit adds high-availability support to the CockroachDB backend. The
locking strategy implemented is heavily influenced from the very similar
Postgres backend.

@hashicorp-cla
Copy link

hashicorp-cla commented Oct 29, 2021

CLA assistant check
All committers have signed the CLA.

@vercel vercel bot temporarily deployed to Preview – vault-storybook October 29, 2021 19:26 Inactive
@vercel vercel bot temporarily deployed to Preview – vault October 29, 2021 19:26 Inactive
@heatherezell
Copy link
Contributor

heatherezell commented Oct 29, 2021

Hi there! Thanks for this submission. Please sign the CLA, and we can get this into review. :)
(Note: If you've signed the CLA at an organizational level, please make sure your organization is publicly viewable, so the CLA bot can pick that up.)
Edit to add: And already signed! Awesome. :)

@vercel vercel bot temporarily deployed to Preview – vault October 29, 2021 19:31 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook October 29, 2021 19:31 Inactive
@vercel vercel bot temporarily deployed to Preview – vault November 16, 2021 19:38 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook November 16, 2021 19:38 Inactive
@DuskEagle
Copy link
Contributor Author

Hi @hsimon-hashicorp , is there anything I can help with to get the review process rolling on this PR?

@heatherezell
Copy link
Contributor

Hi there @DuskEagle - I'll nudge our engineers about this again. Thanks for your patience. :)

Copy link
Collaborator

@ncabatoff ncabatoff left a comment

Choose a reason for hiding this comment

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

Some minor comments/questions, overall it's looking good.

@@ -51,6 +56,11 @@ func NewCockroachDBBackend(conf map[string]string, logger log.Logger) (physical.
return nil, fmt.Errorf("missing connection_url")
}

var haEnabled bool
if haEnabledStr, ok := conf["ha_enabled"]; ok && haEnabledStr == "true" {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is fine as is, but since in Go an absent key looked up in a map results in the zero value, and this is a map[string]string, you can just say haEnabled = conf["ha_enabled"] == "true" if you want to be a bit more succinct.

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 point, I've made the change.

" UPDATE SET (ha_identity, ha_key, ha_value, valid_until) = ($1, $2, $3, NOW() + $4) " +
" WHERE (t.valid_until < NOW() AND t.ha_key = $2) OR " +
" (t.ha_identity = $1 AND t.ha_key = $2) ",
"delete": "DELETE FROM " + dbHATable + " WHERE ha_identity = $1 AND ha_key = $2",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Doesn't hurt to do it this way, and I know you're basing this on the PG storage which does this. But I realized today that we don't need to include ha_identity in this DELETE, since there's a unique index on ha_key. So if you want you could remove that part of the WHERE clause.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good point! I've removed that part of the where clause.

"upsert": "INSERT INTO " + dbHATable + " as t (ha_identity, ha_key, ha_value, valid_until)" +
" VALUES ($1, $2, $3, NOW() + $4) " +
" ON CONFLICT (ha_key) DO " +
" UPDATE SET (ha_identity, ha_key, ha_value, valid_until) = ($1, $2, $3, NOW() + $4) " +
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know little about CockroachDB, but I thought it was fully compatible with PG. You've been diligent about adapting the PG approach here, but I noticed this one discrepancy: instead of NOW() + $4 * INTERVAL '1 seconds' you have simply NOW() + $4. Why's that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What's happening here is that $4 is being passed in as a string type, not an integer type. When we attempt to to $4 * INTERVAL '1 seconds', we get a conversion error:

ERROR: unsupported binary operator: <string> * <interval> (desired <interval>)
SQLSTATE: 22023

We would have the option of casting $4 to an integer, and then multiplying by an interval: NOW() + $4::int * INTERVAL '1 seconds'.

However, $4 is already a value in seconds, and so casting to an int just to multiply by 1 second is unnecessary. It's more succinct to just add the string value directly to NOW().

Copy link
Contributor Author

@DuskEagle DuskEagle left a comment

Choose a reason for hiding this comment

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

Apologies for the delay. I've addressed your comments now :).

@@ -51,6 +56,11 @@ func NewCockroachDBBackend(conf map[string]string, logger log.Logger) (physical.
return nil, fmt.Errorf("missing connection_url")
}

var haEnabled bool
if haEnabledStr, ok := conf["ha_enabled"]; ok && haEnabledStr == "true" {
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 point, I've made the change.

" UPDATE SET (ha_identity, ha_key, ha_value, valid_until) = ($1, $2, $3, NOW() + $4) " +
" WHERE (t.valid_until < NOW() AND t.ha_key = $2) OR " +
" (t.ha_identity = $1 AND t.ha_key = $2) ",
"delete": "DELETE FROM " + dbHATable + " WHERE ha_identity = $1 AND ha_key = $2",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good point! I've removed that part of the where clause.

"upsert": "INSERT INTO " + dbHATable + " as t (ha_identity, ha_key, ha_value, valid_until)" +
" VALUES ($1, $2, $3, NOW() + $4) " +
" ON CONFLICT (ha_key) DO " +
" UPDATE SET (ha_identity, ha_key, ha_value, valid_until) = ($1, $2, $3, NOW() + $4) " +
Copy link
Contributor Author

Choose a reason for hiding this comment

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

What's happening here is that $4 is being passed in as a string type, not an integer type. When we attempt to to $4 * INTERVAL '1 seconds', we get a conversion error:

ERROR: unsupported binary operator: <string> * <interval> (desired <interval>)
SQLSTATE: 22023

We would have the option of casting $4 to an integer, and then multiplying by an interval: NOW() + $4::int * INTERVAL '1 seconds'.

However, $4 is already a value in seconds, and so casting to an int just to multiply by 1 second is unnecessary. It's more succinct to just add the string value directly to NOW().

Copy link
Collaborator

@ncabatoff ncabatoff left a comment

Choose a reason for hiding this comment

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

Looks good! I have a couple of comments that you can choose to address, or not: I'm willing to merge regardless. Let me know how you'd like to proceed.


var (
success = make(chan struct{})
errors = make(chan error)
Copy link
Collaborator

Choose a reason for hiding this comment

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

You might want to make this channel buffered. I saw some output from a static analysis tool recently showing that in the PG implementation, there's an issue here when tryToLock attempts to write to the error channel, but Lock has already returned due to stopCh being closed. In addition, you could make that write to the error channel in Lock tolerate blockage by using the optional send syntax.

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've taken your suggestions and made the channel buffered and the send non-blocking.

@@ -33,7 +34,15 @@ func prepareCockroachDBTestContainer(t *testing.T) (func(), *Config) {
if tableName == "" {
tableName = defaultTableName
}
return func() {}, &Config{*s, "vault." + tableName}
haTableName := os.Getenv("CR_HA_TABLE")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think you need Getenv in this test. I know it's present in some of the existing tests, but it's vestigial mostly.

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've removed the os.Getenv calls for the CR_TABLE and CR_HA_TABLE environment variables, so the tests will use the default values.

This commit adds high-availability support to the CockroachDB backend. The
locking strategy implemented is heavily influenced from the very similar
Postgres backend.
Copy link
Contributor Author

@DuskEagle DuskEagle 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 the review! I've made the changes you recommend, if they look good to you I'd like to see this merged :).


var (
success = make(chan struct{})
errors = make(chan error)
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've taken your suggestions and made the channel buffered and the send non-blocking.

@@ -33,7 +34,15 @@ func prepareCockroachDBTestContainer(t *testing.T) (func(), *Config) {
if tableName == "" {
tableName = defaultTableName
}
return func() {}, &Config{*s, "vault." + tableName}
haTableName := os.Getenv("CR_HA_TABLE")
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've removed the os.Getenv calls for the CR_TABLE and CR_HA_TABLE environment variables, so the tests will use the default values.

@ncabatoff ncabatoff added this to the 1.10.1 milestone Mar 29, 2022
@ncabatoff
Copy link
Collaborator

Not sure why those PG tests are failing in the remote-docker CI tests, but I'm confident it's unrelated to these changes.

@ncabatoff ncabatoff merged commit 7e58f3c into hashicorp:main Mar 29, 2022
@ncabatoff
Copy link
Collaborator

Thanks @DuskEagle !

@DuskEagle
Copy link
Contributor Author

Thank you @ncabatoff !

DuskEagle added a commit to DuskEagle/vault that referenced this pull request Jun 30, 2022
HA support for CockroachDB was added in hashicorp#12965. This commit updates the docs
to reflect that support.
taoism4504 pushed a commit that referenced this pull request Jul 11, 2022
HA support for CockroachDB was added in #12965. This commit updates the docs
to reflect that support.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants