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

CBG-3271 make sure expiration works on a closed bucket #17

Merged
merged 7 commits into from
Nov 27, 2023

Conversation

torcolvin
Copy link
Collaborator

Make sure the following works:

  • open bucket
  • write doc with expiry of 1 sec
  • close bucket
  • wait more than 1 sec

The doc should expire, because another caller can re-open foo, and would expect the document to be deleted. This is akin to metadata purge interval.

Implementation:

  • Abstracted ExpirationManager into a separate struct / file for readability.
  • ExpirationManager exists on every Bucket, since expiration is set based on a CRUD operation, which could occur on any copy of the bucket.
  • The functions for expiration use a new function _underlyingDB which returns a queryable object that is never closed.

Questions:

I really don't like TestExpirationAfterClose because it adds a Sleep to ensure it doesn't panic, and it doubles the test time, which is mostly slow already because TestExpiration has a 3 second sleep. I can't think of a good way to shorten either test, but I think TestExpirationAfterClose might be removed for non development since it repros pretty easily in sync gateway CI.

Make sure the following works:

- open bucket
- write doc with expiry of 1 sec
- close bucket
- wait more than 1 sec

The doc should expire, because another caller can re-open foo, and would
expect the document to be deleted. This is akin to metadata purge
interval.

Implementation:

- Abstracted ExpirationManager into a separate struct / file for
  readability.
- ExpirationManager exists on every Bucket, since expiration is set
  based on a CRUD operation, which could occur on any copy of the
  bucket.
- The functions for expiration use a new function _underlyingDB which
  returns a queryable object that is never closed.
bucket.go Outdated
@@ -203,7 +202,7 @@ func OpenBucket(urlStr string, bucketName string, mode OpenMode) (b *Bucket, err
}

registerBucket(bucket)
return bucket, err
return bucket.copy(), err
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens in the case of concurrent OpenBucket requests for a bucket that doesn't already exist in the registry? It doesn't look like we're doing any locking, so in the case that registerBucket finds an existing bucket in the registry at line 205 (i.e. this instance of OpenBucket lost the race), I think we should be returning a copy of that bucket, and not the newly created one.

bucket_api.go Show resolved Hide resolved
}

// _scheduleExpirationAtOrBefore schedules the next expiration of documents to occur, from the minimum expiration value in the bucket. Requires the mutext to be held.
func (e *expiryManager) _scheduleExpirationAtOrBefore(exp uint32) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like we actually need this version (that requires holding the mutex, based on my other comment) - can this just be moved inside scheduleExpirationAtOrBefore?

@adamcfraser adamcfraser assigned torcolvin and unassigned adamcfraser Nov 20, 2023
close a duplicate bucket if two callers are using OpenBucket at the same
time
}

// registryNewBucket adds a newly opened Bucket to the registry.
func registerNewBucket(bucket *Bucket) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't look like it's needed any longer?

bucket.go Outdated
bucket.Close(ctx)
}
}()

if mode == CreateNew {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think we should pass OpenMode to getCachedBucket to avoid some of the open-then-close we're doing?

if mode == CreateNew {
return nil, fs.ErrExist
}
if urlStr != bucket.url {
return nil, fmt.Errorf("bucket %q already exists at %q, will not open at %q", bucketName, bucket.url, urlStr)
}
registerBucket(bucket)
return bucket, nil

}
Copy link
Contributor

Choose a reason for hiding this comment

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

Does the handling on line 117 to not support ReOpenExisting for inMemory buckets still make sense with the bucket registry/cluster?

// registryBucket adds a newly opened Bucket to the registry.
func (r *bucketRegistry) registerBucket(bucket *Bucket) {
// registryBucket adds a newly opened Bucket to the registry. Returns true if the bucket already exists, and a copy of the bucket to use.
func (r *bucketRegistry) registerBucket(bucket *Bucket) (bool, *Bucket) {
name := bucket.GetName()
debug("registerBucket %v %s at %s", bucket, name, bucket.url)
Copy link
Contributor

Choose a reason for hiding this comment

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

This logging is done in _registerBucket, looks like you could remove it here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done.

@@ -35,17 +35,26 @@ func init() {
}
}

// registryBucket adds a newly opened Bucket to the registry.
func (r *bucketRegistry) registerBucket(bucket *Bucket) {
// registryBucket adds a newly opened Bucket to the registry. Returns true if the bucket already exists, and a copy of the bucket to use.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// registryBucket adds a newly opened Bucket to the registry. Returns true if the bucket already exists, and a copy of the bucket to use.
// registerBucket adds a newly opened Bucket to the registry. Returns true if the bucket already exists, and a copy of the bucket to use.

bucket_registry.go Outdated Show resolved Hide resolved
bucket_registry.go Outdated Show resolved Hide resolved
@torcolvin torcolvin assigned adamcfraser and unassigned torcolvin Nov 21, 2023
@adamcfraser adamcfraser merged commit 2368b8c into main Nov 27, 2023
12 checks passed
@adamcfraser adamcfraser deleted the CBG-3271-fix-expiration branch November 27, 2023 19:17
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.

2 participants