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

release-1.0: Backports and updates for table lease leak #22563

Merged
merged 5 commits into from
Feb 11, 2018

Conversation

bdarnell
Copy link
Contributor

@bdarnell bdarnell commented Feb 10, 2018

Four commits:

  1. Backport util/log: don't panic #17871 to fix a hang when the log disk fills up (unrelated, but requested by a customer)
  2. Backport fix for storage: avoid reading uncommitted tail of Raft log when becoming leader #18601, which fixes the endless election loop for ranges that have been affected by this bug
  3. Address sql: no throttling for releasing table leases #20451. This is new work in this branch, although this change should be propagated to the other branches
  4. A new 1.0-specific fix for the table lease leak sql: table lease leakage in 1.0.6 #20422.

@bdarnell bdarnell requested a review from a team February 10, 2018 17:18
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@bdarnell
Copy link
Contributor Author

bdarnell commented Feb 10, 2018

@vivekmenezes Please look at the last commit.

@bdarnell
Copy link
Contributor Author

@cockroachdb/build-prs I patched etcd directly in our vendored repo, which our linter is unhappy with. What should I be doing instead?

@benesch
Copy link
Contributor

benesch commented Feb 10, 2018 via email

@petermattis
Copy link
Collaborator

Review status: 0 of 4 files reviewed at latest revision, all discussions resolved, some commit checks failed.


pkg/sql/lease.go, line 577 at r4 (raw file):

			// it's our responsibility to delete it when we make it no
			// longer the newest.
			defer func(lease *LeaseState) {

Creating defers in a loop is suspicious, though I'm not seeing anything wrong, I'm also not particularly familiar with this code anymore. Is there a reason you're doing this with a defer? I'm also not clear on why this is only done if a new lease was acquired. Questions all over the place, mostly indicating my deficiencies.

How did you test this?


Comments from Reviewable

@bdarnell
Copy link
Contributor Author

Review status: 0 of 4 files reviewed at latest revision, 1 unresolved discussion, some commit checks failed.


pkg/sql/lease.go, line 577 at r4 (raw file):

Previously, petermattis (Peter Mattis) wrote…

Creating defers in a loop is suspicious, though I'm not seeing anything wrong, I'm also not particularly familiar with this code anymore. Is there a reason you're doing this with a defer? I'm also not clear on why this is only done if a new lease was acquired. Questions all over the place, mostly indicating my deficiencies.

How did you test this?

I haven't tested it yet (aside from running make test). I'm not entirely sure how to test it.

Moving this out of a defer is a good idea. I'll set a local variable here and move the rest of the code below acquireFromStoreLocked.

The idea behind this change is that we're using a slightly non-standard refcounting pattern. Only the newest lease is allowed to increase its refcount (and as an optimization, the newest lease continues to exist even while its refcount is zero, instead of being destroyed and recreated); older leases are deleted when their refcounts reach zero. If we created a new lease, we've transitioned the previous one from "newest" to "not newest". If its refcount is already zero, no one else will be coming along to clean it up so we have to do it here.


Comments from Reviewable

@petermattis
Copy link
Collaborator

Review status: 0 of 4 files reviewed at latest revision, 1 unresolved discussion, some commit checks failed.


pkg/sql/lease.go, line 577 at r4 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

I haven't tested it yet (aside from running make test). I'm not entirely sure how to test it.

Moving this out of a defer is a good idea. I'll set a local variable here and move the rest of the code below acquireFromStoreLocked.

The idea behind this change is that we're using a slightly non-standard refcounting pattern. Only the newest lease is allowed to increase its refcount (and as an optimization, the newest lease continues to exist even while its refcount is zero, instead of being destroyed and recreated); older leases are deleted when their refcounts reach zero. If we created a new lease, we've transitioned the previous one from "newest" to "not newest". If its refcount is already zero, no one else will be coming along to clean it up so we have to do it here.

Ok, I need to look at this in more detail later. For testing, you could manually replicate what I did in #20422 (comment).


Comments from Reviewable

@bdarnell bdarnell force-pushed the 1.0-backports branch 2 times, most recently from fa0014a to acd96e2 Compare February 10, 2018 19:38
benesch and others added 2 commits February 10, 2018 14:48
Previously, log.outputLogEntry could panic while holding the log mutex.
This would deadlock any goroutine that logged while recovering from the
panic, which is approximately all of the recover routines. Most
annoyingly, the crash reporter would deadlock, swallowing the cause of
the panic.

Avoid panicking while holding the log mutex and use l.exit instead,
which exists for this very purpose. In the process, enforce the
invariant that l.mu is held when l.exit is called. (The previous
behavior was, in fact, incorrect, as l.flushAll should not be called
without holding l.mu.)

Also add a Tcl test to ensure this doesn't break in the future.
@bdarnell bdarnell force-pushed the 1.0-backports branch 2 times, most recently from 9fc6047 to 5c3d46c Compare February 10, 2018 21:52
@bdarnell
Copy link
Contributor Author

bdarnell commented Feb 10, 2018

OK, I've tested manually with the procedure from #20422 (comment) and verified that the number of leases holds steady (although the count varies between 4 and 6 for the 5-node cluster, spending more time at 4 than I'd expect Nevermind, this was a 4-node cluster. I was confused by the way roachprod uses the last machine only for load generation).

I've also done the necessary glide (not dep in 1.0) magic to make the linter happy.

@petermattis
Copy link
Collaborator

Review status: 0 of 6 files reviewed at latest revision, all discussions resolved, some commit checks failed.


pkg/sql/lease.go, line 820 at r8 (raw file):

	// Release to the store asynchronously, without the tableState lock.
	if err := t.stopper.RunLimitedAsyncTask(ctx, removeLeaseSem, true,

This will make removeLease block holding exitingLease.mu. Seems like that could be problematic.


Comments from Reviewable

@bdarnell
Copy link
Contributor Author

Review status: 0 of 6 files reviewed at latest revision, 1 unresolved discussion, some commit checks failed.


pkg/sql/lease.go, line 820 at r8 (raw file):

Previously, petermattis (Peter Mattis) wrote…

This will make removeLease block holding exitingLease.mu. Seems like that could be problematic.

Maybe. But under normal conditions the number of leases will be small and the limit will rarely be reached (I could increase the limit to make this less likely). Do you think it's worth doing anything more clever on the 1.0 branch?


Comments from Reviewable

Copy link
Contributor

@vivekmenezes vivekmenezes left a comment

Choose a reason for hiding this comment

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

LGTM

pkg/sql/lease.go Outdated
if err := t.stopper.RunAsyncTask(ctx, func(ctx context.Context) {
m.LeaseStore.Release(ctx, t.stopper, lease)
}); err != nil {
if err := t.stopper.RunLimitedAsyncTask(ctx, removeLeaseSem, true,
Copy link
Contributor

Choose a reason for hiding this comment

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

I worry about changing this to use the RunLimitedAsyncTask() because it blocks while holding on to the lock over all leases for a table. I think 1.0 users are likely to use very few tables and so it's very likely there will be only a few of these async tasks created.

@petermattis
Copy link
Collaborator

petermattis commented Feb 11, 2018 via email

Addresses cockroachdb#20451 for the release-1.0 branch
This prevents a leak (only present in 1.0) of these leases, which
could accumulate into a huge amount of work when PurgeOldLeases is
called.

Fixes cockroachdb#20422
Newer branches have a more sophisticated solution for this (cockroachdb#20542)
@petermattis
Copy link
Collaborator

:lgtm:


Review status: 0 of 7 files reviewed at latest revision, 2 unresolved discussions, all commit checks successful.


pkg/sql/lease.go, line 820 at r8 (raw file):

Previously, bdarnell (Ben Darnell) wrote…

Maybe. But under normal conditions the number of leases will be small and the limit will rarely be reached (I could increase the limit to make this less likely). Do you think it's worth doing anything more clever on the 1.0 branch?

Ack, this looks safer. If you see any test failures you'd need to combine this with watching for watching the stopper done channel.


Comments from Reviewable

@bdarnell bdarnell merged commit b277f36 into cockroachdb:release-1.0 Feb 11, 2018
@bdarnell bdarnell deleted the 1.0-backports branch February 11, 2018 17:18
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