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

HDDS-2779. Fix list volume for --start parameter #385

Merged
merged 16 commits into from
Jan 9, 2020

Conversation

cxorm
Copy link
Member

@cxorm cxorm commented Dec 21, 2019

What changes were proposed in this pull request?

This PR was created to let --start work well in listing volume.
(In command ozone sh vol list --start=<startVolume>)

What is the link to the Apache JIRA

https://issues.apache.org/jira/browse/HDDS-2779

How was this patch tested?

  1. UTs is updated, and ran.
  2. Ran on standalone cluster.

Comment on lines 302 to 306
if(!currentIterator.hasNext()) {
currentIterator = getNextListOfVolumes(
currentValue != null ? currentValue.getName() : null)
.iterator();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Without this logic, remote iteration will not work. Removing this will break the listVolume call if we try to list more than 1000 (ozone.client.list.cache ) volumes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks @nandakumar131 for this important info.

I deleted the block cause it would lead infinitely loop when I test listVolumes on my cluster.
I'm going to fix it. (without deleting it.)

Copy link
Member Author

@cxorm cxorm Dec 23, 2019

Choose a reason for hiding this comment

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

Fixed with checking existed volume.
And work well in listing more than 1000 volumes with the same user.

boolean startKeyFound = Strings.isNullOrEmpty(startKey);
if (!startKeyFound) {
OmVolumeArgs startVolArgs = getVolumeTable()
Copy link
Contributor

Choose a reason for hiding this comment

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

This will break the case where the user provides "/" at the start of volume name, we should also handle that case.

if (startVolArgs == null) {
throw new OMException("StartVolume info not found for " + startKey,
ResultCodes.VOLUME_NOT_FOUND);
} else if (!startKey.startsWith(prefix)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should also handle cases where the startKey doesn't match the given prefix.

@nandakumar131
Copy link
Contributor

Thanks @cxorm for working on this.

Suggestion:
You can simplify listVolumes code with the below one

public List<OmVolumeArgs> listVolumes(String userName,
      String prefix, String startKey, int maxKeys) throws IOException {

    if (StringUtil.isBlank(userName)) {
      throw new OMException("User name is required to list Volumes.",
          ResultCodes.USER_NOT_FOUND);
    }

    final List<OmVolumeArgs> result = Lists.newArrayList();
    final List<String> volumes = getVolumesByUser(userName)
        .getVolumeNamesList();

    int index = 0;
    if (!Strings.isNullOrEmpty(startKey)) {
      index = volumes.indexOf(startKey.startsWith(OzoneConsts.OM_KEY_PREFIX) ?
          startKey.substring(1) : startKey);
    }
    final String startChar = prefix == null ? "" : prefix;

    while (index != -1 && index < volumes.size() && result.size() < maxKeys) {
      final String volumeName = volumes.get(index);
      if (volumeName.startsWith(startChar)) {
        final OmVolumeArgs volumeArgs = getVolumeTable()
            .get(getVolumeKey(volumeName));
        if (volumeArgs == null) {
          // Could not get volume info by given volume name,
          // since the volume name is loaded from db,
          // this probably means om db is corrupted or some entries are
          // accidentally removed.
          throw new OMException("Volume info not found for " + volumeName,
              ResultCodes.VOLUME_NOT_FOUND);
        }
        result.add(volumeArgs);
      }
      index++;
    }
    return result;
  }

@cxorm
Copy link
Member Author

cxorm commented Dec 23, 2019

Thanks @cxorm for working on this.

Suggestion:
You can simplify listVolumes code with the below one

public List<OmVolumeArgs> listVolumes(String userName,
      String prefix, String startKey, int maxKeys) throws IOException {

    if (StringUtil.isBlank(userName)) {
      throw new OMException("User name is required to list Volumes.",
          ResultCodes.USER_NOT_FOUND);
    }

    final List<OmVolumeArgs> result = Lists.newArrayList();
    final List<String> volumes = getVolumesByUser(userName)
        .getVolumeNamesList();

    int index = 0;
    if (!Strings.isNullOrEmpty(startKey)) {
      index = volumes.indexOf(startKey.startsWith(OzoneConsts.OM_KEY_PREFIX) ?
          startKey.substring(1) : startKey);
    }
    final String startChar = prefix == null ? "" : prefix;

    while (index != -1 && index < volumes.size() && result.size() < maxKeys) {
      final String volumeName = volumes.get(index);
      if (volumeName.startsWith(startChar)) {
        final OmVolumeArgs volumeArgs = getVolumeTable()
            .get(getVolumeKey(volumeName));
        if (volumeArgs == null) {
          // Could not get volume info by given volume name,
          // since the volume name is loaded from db,
          // this probably means om db is corrupted or some entries are
          // accidentally removed.
          throw new OMException("Volume info not found for " + volumeName,
              ResultCodes.VOLUME_NOT_FOUND);
        }
        result.add(volumeArgs);
      }
      index++;
    }
    return result;
  }

Thanks @nandakumar131 for the suggestion.
Updated and ran the UT.

@cxorm
Copy link
Member Author

cxorm commented Dec 24, 2019

Sorry, there is something wrong when I test.
I'm going to fix it.

@cxorm
Copy link
Member Author

cxorm commented Dec 24, 2019

After fixing listVolumes with startVolume, the original VolumeIterator#getNextListOfVolumes would always have a non-empty list, and this situation leads to VolumeIterator#hasNext always be true.

So here we let the VolumeIterator#getNextListOfVolumes be empty list when there is no volume to be listed.

Fixed, and ran on my standalone cluster.

@nandakumar131
Copy link
Contributor

After fixing listVolumes with startVolume, the original VolumeIterator#getNextListOfVolumes would always have a non-empty list, and this situation leads to VolumeIterator#hasNext always be true.

So here we let the VolumeIterator#getNextListOfVolumes be empty list when there is no volume to be listed.

The proper fix is to not return the startVolume as part of the result. If we do that, we don't need two different methods getInitListOfVolumes/getNextListOfVolumes for getting list of volumes.

index = volumes.indexOf(
startKey.startsWith(OzoneConsts.OM_KEY_PREFIX) ?
startKey.substring(1) :
startKey);
Copy link
Contributor

Choose a reason for hiding this comment

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

Add index = index != -1 ? index + 1 : index; at line 826 to make sure that the startKey is not part of the returned result.

Copy link
Contributor

Choose a reason for hiding this comment

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

With this change, hopefully we don't need the changes made in ObjectStore class. Things should work as expected.

@cxorm
Copy link
Member Author

cxorm commented Jan 4, 2020

After fixing listVolumes with startVolume, the original VolumeIterator#getNextListOfVolumes would always have a non-empty list, and this situation leads to VolumeIterator#hasNext always be true.
So here we let the VolumeIterator#getNextListOfVolumes be empty list when there is no volume to be listed.

The proper fix is to not return the startVolume as part of the result. If we do that, we don't need two different methods getInitListOfVolumes/getNextListOfVolumes for getting list of volumes.

Thanks @nandakumar131 for the review.

ObjectStore change were reverted, and we exclude startVolume as part of listing result for now.

Sorry about a little question,
I found that if we set --start with the last volume,
the list will contain all volumes instead of empty result (like the --start behavior in listing buckets).
Would we fix it ? (or the behavior makes sense ?)

@nandakumar131
Copy link
Contributor

I found that if we set --start with the last volume,
the list will contain all volumes instead of empty result (like the --start behavior in listing buckets).
Would we fix it ? (or the behavior makes sense ?)

Thanks @cxorm for digging it. Yes, we should fix it.
If we set --start with the last volume, the result should be an empty list.

@cxorm
Copy link
Member Author

cxorm commented Jan 6, 2020

I found that if we set --start with the last volume,
the list will contain all volumes instead of empty result (like the --start behavior in listing buckets).
Would we fix it ? (or the behavior makes sense ?)

Thanks @cxorm for digging it. Yes, we should fix it.
If we set --start with the last volume, the result should be an empty list.

Thanks @nandakumar131 for the comment.

I think the currentValue in this line remaining null if and only if we get empty list in this line.

The first round of hasNext() is called and enter the if statement (cause the empty-list), and then execute currentIterator = getNextListOfVolumes(null).iterator(); that resulting in listing all volumes.

So fix was addressed this part. (The same as listing buckets, I'm going to create a Jira for it.)

@xiaoyuyao
Copy link
Contributor

Let's discuss the list --start behavior on slack.

@cxorm
Copy link
Member Author

cxorm commented Jan 9, 2020

Thanks @xiaoyuyao for the comment.
Here is the discussion about this issue in slack.

@nandakumar131
Copy link
Contributor

I found that if we set --start with the last volume,
the list will contain all volumes instead of empty result (like the --start behavior in listing buckets).
Would we fix it ? (or the behavior makes sense ?)

Thanks @cxorm for digging it. Yes, we should fix it.
If we set --start with the last volume, the result should be an empty list.

Thanks @nandakumar131 for the comment.

I think the currentValue in this line remaining null if and only if we get empty list in this line.

The first round of hasNext() is called and enter the if statement (cause the empty-list), and then execute currentIterator = getNextListOfVolumes(null).iterator(); that resulting in listing all volumes.

So fix was addressed this part. (The same as listing buckets, I'm going to create a Jira for it.)

Makes sense, thanks for fixing it.

@nandakumar131
Copy link
Contributor

Thanks @cxorm for the contribution and thanks @xiaoyuyao for the review. I will merge this shortly.

@nandakumar131 nandakumar131 merged commit 2fa37ef into apache:master Jan 9, 2020
@cxorm
Copy link
Member Author

cxorm commented Jan 10, 2020

Thanks @nandakumar131 and @xiaoyuyao for the reviews.
Thanks @nandakumar131 for the merge.

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.

3 participants