Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
44cb5ba
add Freon mix workload test command
Aug 25, 2022
c026a4e
update
Aug 26, 2022
202e36c
update mix workload test
Aug 29, 2022
31188a2
update mix workload Freon command
Aug 30, 2022
a4383bf
Update mix workload Freon command class
Aug 31, 2022
a16bb53
fix compiling error
Sep 1, 2022
d8ceca7
Add robot test for read/write keys operation
Sep 2, 2022
8e69bc5
remove testing Freon class:
Sep 2, 2022
2e8e03c
handle read/write key exception
Sep 2, 2022
24b4e1f
remove additional LOG variable
Sep 2, 2022
95442e8
Update mix workload Freon command
Sep 7, 2022
d3f01ef
Freon test ozone mix workload
Sep 7, 2022
58c809c
Remove debug logs
Sep 7, 2022
68ee21a
Fix code style
Sep 9, 2022
f7f1dd6
Update robot test
Sep 14, 2022
244e855
Add multi-clients support
Sep 16, 2022
25d6057
Fix checkstyle error
Sep 16, 2022
7cc0685
Add name for range keys generator
Sep 16, 2022
4651529
set md5 as default algorithm to calculate unsorted key name; disable …
Sep 20, 2022
102cb3c
Comment out unused block-token import; ignore block-token unit tests
Sep 20, 2022
0d259f9
Update CLI option name;Use enum for read/write task-type;use proxy to…
Sep 22, 2022
0db071e
Revert distabled block-token generation
Sep 26, 2022
552946f
Remove debug message
Sep 26, 2022
d1eead4
Fix checkstyle error;Update r/w Freon robot test command
Sep 26, 2022
c58592a
Update command description; update robot test
Oct 11, 2022
6940330
Update robot test
Oct 12, 2022
709e36b
Update Freon command
Oct 12, 2022
d42cd4b
Update Freon command
Oct 12, 2022
9105502
Update robot test
Oct 15, 2022
d9c3c4b
fix checkStyle error, findbug error
Oct 20, 2022
400e92f
Update Freon robot test
Oct 21, 2022
cfba04e
Update var name in RangeKeysGenerator
Oct 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions hadoop-ozone/dist/src/main/smoketest/freon/read-write-key.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

*** Settings ***
Documentation Test freon read/write key commands
Resource ../ozone-lib/freon.robot
Test Timeout 5 minutes

*** Variables ***
${PREFIX} ${EMPTY}


*** Test Cases ***
Pre-generate 100 keys of size 1 byte each to Ozone
${result} = Execute ozone freon ork -n 1 -t 10 -r 100 --size 1 -v voltest -b buckettest -p performanceTest

Read 10 keys from pre-generated keys
${keysCount} = BuiltIn.Set Variable 10
${result} = Execute ozone freon ockrw -n ${keysCount} -t 10 -r 100 -v voltest -b buckettest -p performanceTest
Should contain ${result} Successful executions: ${keysCount}

Read 10 keys' metadata from pre-generated keys
${keysCount} = BuiltIn.Set Variable 10
${result} = Execute ozone freon ockrw -n ${keysCount} -t 10 -m -r 100 -v voltest -b buckettest -p performanceTest
Should contain ${result} Successful executions: ${keysCount}

Write 10 keys of size 1 byte each from key index 0 to 99
${keysCount} = BuiltIn.Set Variable 10
${size} = BuiltIn.Set Variable 1
${result} = Execute ozone freon ockrw -n ${keysCount} -t 10 --percentage-read 0 --size ${size} -r 100 -v voltest -b buckettest -p performanceTest
Should contain ${result} Successful executions: ${keysCount}
${keyName} = Execute echo -n '1' | md5sum | head -c 7
${result} = Execute ozone sh key info /voltest/buckettest/performanceTest/${keyName}
Should contain ${result} \"dataSize\" : 1


Run 90 % of read-key tasks and 10 % of write-key tasks for 10 keys from pre-generated keys
${keysCount} = BuiltIn.Set Variable 10
${result} = Execute ozone freon ockrw -n ${keysCount} -t 10 --percentage-read 90 -r 100 -v voltest -b buckettest -p performanceTest
Should contain ${result} Successful executions: ${keysCount}

Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
OmBucketReadWriteFileOps.class,
OmBucketReadWriteKeyOps.class,
OmRPCLoadGenerator.class,
OzoneClientKeyReadWriteOps.class,
RangeKeysGenerator.class
},
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.hadoop.ozone.freon;

import org.apache.commons.codec.digest.DigestUtils;

import java.util.function.Function;

/**
* Utility class to generate key name from a given key index.
*/
public class KeyGeneratorUtil {
public static final String PURE_INDEX = "pureIndex";
public static final String MD5 = "md5";
public static final String FILE_DIR_SEPARATOR = "/";

public String generatePureIndexKeyName(int number) {
return String.valueOf(number);
}
public Function<Integer, String> pureIndexKeyNameFunc() {
return number -> String.valueOf(number);
}

public String generateMd5KeyName(int number) {
String encodedStr = DigestUtils.md5Hex(String.valueOf(number));
return encodedStr.substring(0, 7);
}

public Function<Integer, String> md5KeyNameFunc() {
return number -> DigestUtils.md5Hex(String.valueOf(number)).substring(0, 7);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.hadoop.ozone.freon;


import com.codahale.metrics.Timer;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.RandomUtils;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.client.OzoneKeyDetails;
import org.apache.hadoop.ozone.client.io.OzoneInputStream;
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadLocalRandom;
import java.util.HashMap;

import static org.apache.hadoop.ozone.freon.KeyGeneratorUtil.FILE_DIR_SEPARATOR;

/**
* Ozone key generator/reader for performance test.
*/

@CommandLine.Command(name = "ockrw",
aliases = "ozone-client-key-read-write-ops",
description = "Generate keys with a fixed name and ranges that can"
+ " be written and read as sub-ranges from multiple clients.",
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true,
showDefaultValues = true)
public class OzoneClientKeyReadWriteOps extends BaseFreonGenerator
implements Callable<Void> {

@CommandLine.Option(names = {"-v", "--volume"},
description = "Name of the volume which contains the test data. " +
"Will be created if missing.",
defaultValue = "ockrwvolume")
private String volumeName;

@CommandLine.Option(names = {"-b", "--bucket"},
description = "Name of the bucket which contains the test data.",
defaultValue = "ockrwbucket")
private String bucketName;

@CommandLine.Option(names = {"-m", "--read-metadata-only"},
description = "If only read key's metadata.",
defaultValue = "false")
private boolean readMetadataOnly;

@CommandLine.Option(names = {"-s", "--start-index"},
description = "Start index of keys of read/write operation." +
"This can allow adding keys incrementally or parallel from multiple"
+ " clients. Example: Write keys 0-1000000 followed by "
+ "keys 1000001-2000000.",
defaultValue = "0")
private int startIndex;

@CommandLine.Option(names = {"-r", "--range"},
description = "Range of read/write operations. This in co-ordination"
+ " with --start-index can specify the range to read. "
+ "Example: Read from --start-index 1000 and read --range 1000 keys.",
defaultValue = "1")
private int range;

@CommandLine.Option(names = {"--size"},
description = "Object size (in bytes) " +
"to read/write. If user sets a read size which is larger"
+ " than the key size, it only reads bytes up to key size.",
defaultValue = "1")
private int objectSizeInBytes;

@CommandLine.Option(names = {"--contiguous"},
description = "By default, the keys are randomized lexically"
+ " by calculating the md5 of the key name. If this option is set,"
+ " the keys are written lexically contiguously.",
defaultValue = "false")
private boolean keySorted;

@CommandLine.Option(names = {"--percentage-read"},
description = "Percentage of read tasks in mix workload."
+ " The remainder of the percentage will writes to keys."
+ " Example --percentage-read 90 will result in 10% writes.",
defaultValue = "100")
private int percentageRead;

@CommandLine.Option(
names = "--om-service-id",
description = "OM Service ID"
)
private String omServiceID = null;

private Timer timer;

private OzoneClient[] ozoneClients;

private int clientCount;

private byte[] keyContent;

private static final Logger LOG =
LoggerFactory.getLogger(OzoneClientKeyReadWriteOps.class);

/**
* Task type of read task, or write task.
*/
public enum TaskType {
READ_TASK,
WRITE_TASK
}
private KeyGeneratorUtil kg;


@Override
public Void call() throws Exception {
init();
OzoneConfiguration ozoneConfiguration = createOzoneConfiguration();
clientCount = getThreadNo();
ozoneClients = new OzoneClient[clientCount];
for (int i = 0; i < clientCount; i++) {
ozoneClients[i] = createOzoneClient(omServiceID, ozoneConfiguration);
}

ensureVolumeAndBucketExist(ozoneClients[0], volumeName, bucketName);

timer = getMetrics().timer("key-read-write");
if (objectSizeInBytes >= 0) {
keyContent = RandomUtils.nextBytes(objectSizeInBytes);
}
if (kg == null) {
kg = new KeyGeneratorUtil();
}
runTests(this::readWriteKeys);

for (int i = 0; i < clientCount; i++) {
if (ozoneClients[i] != null) {
ozoneClients[i].close();
}
}
return null;
}

public void readWriteKeys(long counter) throws RuntimeException, IOException {
int clientIndex = (int)((counter) % clientCount);
TaskType taskType = decideReadOrWriteTask();
String keyName = getKeyName();

timer.time(() -> {
try {
switch (taskType) {
case READ_TASK:
processReadTasks(keyName, ozoneClients[clientIndex]);
break;
case WRITE_TASK:
processWriteTasks(keyName, ozoneClients[clientIndex]);
break;
default:
break;
}
} catch (RuntimeException ex) {
LOG.error(ex.getMessage());
throw ex;
} catch (IOException ex) {
LOG.error(ex.getMessage());
throw new RuntimeException(ex.getMessage());
}

});
}
@SuppressFBWarnings
public void processReadTasks(String keyName, OzoneClient client)
throws RuntimeException, IOException {
OzoneKeyDetails keyDetails = client.getProxy().
getKeyDetails(volumeName, bucketName, keyName);
if (!readMetadataOnly) {
byte[] data = new byte[objectSizeInBytes];
try (OzoneInputStream introStream = keyDetails.getContent()) {
introStream.read(data);
} catch (Exception ex) {
throw ex;
}
}
}

public void processWriteTasks(String keyName, OzoneClient ozoneClient)
throws RuntimeException, IOException {
try (OzoneOutputStream out =
ozoneClient.getProxy().createKey(volumeName, bucketName,
keyName, objectSizeInBytes, null, new HashMap())) {
out.write(keyContent);
} catch (Exception ex) {
throw ex;
}
}

public TaskType decideReadOrWriteTask() {
if (percentageRead == 100) {
return TaskType.READ_TASK;
} else if (percentageRead == 0) {
return TaskType.WRITE_TASK;
}
//mix workload
int tmp = ThreadLocalRandom.current().nextInt(1, 101);
if (tmp <= percentageRead) {
return TaskType.READ_TASK;
} else {
return TaskType.WRITE_TASK;
}
}

public String getKeyName() {
StringBuilder keyNameSb = new StringBuilder();
int randomIdxWithinRange = ThreadLocalRandom.current().
nextInt(startIndex, startIndex + range);

if (keySorted) {
keyNameSb.append(getPrefix()).append(FILE_DIR_SEPARATOR).
append(randomIdxWithinRange);
} else {
keyNameSb.append(getPrefix()).append(FILE_DIR_SEPARATOR).
append(kg.generateMd5KeyName(randomIdxWithinRange));
}
return keyNameSb.toString();
}

}
Loading