forked from TrueCloudLab/xk6-frostfs
[#19] Add counters to verify scenario
Signed-off-by: Vladimir Domnich <v.domnich@yadro.com>
This commit is contained in:
parent
2d4e619992
commit
bf4a11474e
3 changed files with 79 additions and 27 deletions
|
@ -104,6 +104,32 @@ func (o *ObjRegistry) SetObjectStatus(id uint64, newStatus string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *ObjRegistry) GetObjectCountInStatus(status string) (int, error) {
|
||||||
|
var objCount = 0
|
||||||
|
err := o.boltDB.View(func(tx *bbolt.Tx) error {
|
||||||
|
b := tx.Bucket([]byte(bucketName))
|
||||||
|
if b == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := b.Cursor()
|
||||||
|
for keyBytes, objBytes := c.First(); keyBytes != nil; keyBytes, objBytes = c.Next() {
|
||||||
|
if objBytes != nil {
|
||||||
|
var obj ObjectInfo
|
||||||
|
if err := json.Unmarshal(objBytes, &obj); err != nil {
|
||||||
|
// Ignore malformed objects
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if obj.Status == status {
|
||||||
|
objCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return objCount, err
|
||||||
|
}
|
||||||
|
|
||||||
func (o *ObjRegistry) NextObjectToVerify() (*ObjectInfo, error) {
|
func (o *ObjRegistry) NextObjectToVerify() (*ObjectInfo, error) {
|
||||||
return o.objSelector.NextObject()
|
return o.objSelector.NextObject()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ Options:
|
||||||
* WRITE_OBJ_SIZE - object size in kb for write(PUT) operations
|
* WRITE_OBJ_SIZE - object size in kb for write(PUT) operations
|
||||||
* PREGEN_JSON - path to json file with pre-generated containers and objects
|
* PREGEN_JSON - path to json file with pre-generated containers and objects
|
||||||
* REGISTRY_FILE - if set, all produced objects will be stored in database for subsequent verification. Database file name will be set to value of REGISTRY_FILE.
|
* REGISTRY_FILE - if set, all produced objects will be stored in database for subsequent verification. Database file name will be set to value of REGISTRY_FILE.
|
||||||
|
* SLEEP - time interval (in seconds) between VU iterations.
|
||||||
|
|
||||||
## S3
|
## S3
|
||||||
|
|
||||||
|
@ -73,9 +74,10 @@ To verify stored objects execute scenario with options:
|
||||||
```
|
```
|
||||||
|
|
||||||
Scenario picks up all objects in `created` status. If object is stored correctly, its' status will be changed into `verified`. If object does not exist or its' data is corrupted, then the status will be changed into `invalid`.
|
Scenario picks up all objects in `created` status. If object is stored correctly, its' status will be changed into `verified`. If object does not exist or its' data is corrupted, then the status will be changed into `invalid`.
|
||||||
Scenario ends as soon as all objects are checked (return code will be [108](https://k6.io/docs/javascript-api/k6-execution/#test)).
|
Scenario ends as soon as all objects are checked or time limit is exceeded.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
* CLIENTS - number of VUs for verifying objects (VU can handle both GRPC and S3 objects)
|
* CLIENTS - number of VUs for verifying objects (VU can handle both GRPC and S3 objects)
|
||||||
* TIME_LIMIT - amount of time in seconds that is sufficient to verify all objects. If this time interval ends, then verification process will be interrupted and objects that have not been checked will stay in the `created` state.
|
* TIME_LIMIT - amount of time in seconds that is sufficient to verify all objects. If this time interval ends, then verification process will be interrupted and objects that have not been checked will stay in the `created` state.
|
||||||
* REGISTRY_FILE - database file from which objects for verification should be read.
|
* REGISTRY_FILE - database file from which objects for verification should be read.
|
||||||
|
* SLEEP - time interval (in seconds) between VU iterations.
|
|
@ -2,18 +2,24 @@ import native from 'k6/x/neofs/native';
|
||||||
import registry from 'k6/x/neofs/registry';
|
import registry from 'k6/x/neofs/registry';
|
||||||
import s3 from 'k6/x/neofs/s3';
|
import s3 from 'k6/x/neofs/s3';
|
||||||
import { sleep } from 'k6';
|
import { sleep } from 'k6';
|
||||||
import exec from 'k6/execution';
|
import { Counter } from 'k6/metrics';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
./k6 run -e CLIENTS=200 -e TIME_LIMIT=30 -e GRPC_ENDPOINTS=node4.data:8084
|
./k6 run -e CLIENTS=200 -e TIME_LIMIT=30 -e GRPC_ENDPOINTS=node4.data:8084
|
||||||
-e REGISTRY_FILE=registry.bolt scenarios/verify.js
|
-e REGISTRY_FILE=registry.bolt scenarios/verify.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const obj_registry = registry.open(__ENV.REGISTRY_FILE);
|
const obj_registry = registry.open(__ENV.REGISTRY_FILE);
|
||||||
|
|
||||||
// Time limit (in seconds) for the run
|
// Time limit (in seconds) for the run
|
||||||
const time_limit = __ENV.TIME_LIMIT || "60";
|
const time_limit = __ENV.TIME_LIMIT || "60";
|
||||||
|
|
||||||
|
// Count of objects in each status
|
||||||
|
const obj_counters = {
|
||||||
|
verified: new Counter('verified_obj'),
|
||||||
|
skipped: new Counter('skipped_obj'),
|
||||||
|
invalid: new Counter('invalid_obj'),
|
||||||
|
};
|
||||||
|
|
||||||
// Connect to random gRPC endpoint
|
// Connect to random gRPC endpoint
|
||||||
let grpc_client = undefined;
|
let grpc_client = undefined;
|
||||||
if (__ENV.GRPC_ENDPOINTS) {
|
if (__ENV.GRPC_ENDPOINTS) {
|
||||||
|
@ -30,11 +36,15 @@ if (__ENV.S3_ENDPOINTS) {
|
||||||
s3_client = s3.connect(`http://${s3_endpoint}`);
|
s3_client = s3.connect(`http://${s3_endpoint}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will attempt to verify every object in "created" status. The scenario will execute
|
||||||
|
// as many scenarios as there are objects. Each object will have 3 retries to be verified
|
||||||
|
const obj_count_to_verify = obj_registry.getObjectCountInStatus("created");
|
||||||
const scenarios = {
|
const scenarios = {
|
||||||
verify: {
|
verify: {
|
||||||
executor: 'constant-vus',
|
executor: 'shared-iterations',
|
||||||
vus: __ENV.CLIENTS,
|
vus: __ENV.CLIENTS,
|
||||||
duration: `${time_limit}s`,
|
iterations: obj_count_to_verify,
|
||||||
|
maxDuration: `${time_limit}s`,
|
||||||
exec: 'obj_verify',
|
exec: 'obj_verify',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
}
|
}
|
||||||
|
@ -45,6 +55,13 @@ export const options = {
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
// Populate counters with initial values
|
||||||
|
for (const [status, counter] of Object.entries(obj_counters)) {
|
||||||
|
counter.add(obj_registry.getObjectCountInStatus(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function obj_verify() {
|
export function obj_verify() {
|
||||||
if (__ENV.SLEEP) {
|
if (__ENV.SLEEP) {
|
||||||
sleep(__ENV.SLEEP);
|
sleep(__ENV.SLEEP);
|
||||||
|
@ -52,31 +69,38 @@ export function obj_verify() {
|
||||||
|
|
||||||
const obj = obj_registry.nextObjectToVerify();
|
const obj = obj_registry.nextObjectToVerify();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
// TODO: consider using a metric with abort condition to stop execution when
|
console.log("All objects have been verified");
|
||||||
// all VUs have no objects to verify. Alternative solution could be a
|
return;
|
||||||
// shared-iterations executor, but it might be not a good choice, as we need to
|
|
||||||
// check same object several times (if specific request fails)
|
|
||||||
|
|
||||||
// Allow time for other VUs to complete verification
|
|
||||||
sleep(30.0);
|
|
||||||
exec.test.abort("All objects have been verified");
|
|
||||||
}
|
}
|
||||||
console.log(`Verifying object ${obj.id}`);
|
console.log(`Verifying object ${obj.id}`);
|
||||||
|
|
||||||
let result = undefined;
|
const obj_status = verify_object_with_retries(obj, 3);
|
||||||
if (obj.c_id && obj.o_id) {
|
obj_counters[obj_status].add(1);
|
||||||
result = grpc_client.verifyHash(obj.c_id, obj.o_id, obj.payload_hash);
|
obj_registry.setObjectStatus(obj.id, obj_status);
|
||||||
} else if (obj.s3_bucket && obj.s3_key) {
|
}
|
||||||
result = s3_client.verifyHash(obj.s3_bucket, obj.s3_key, obj.payload_hash);
|
|
||||||
} else {
|
function verify_object_with_retries(obj, attempts) {
|
||||||
console.log(`Object id=${obj.id} cannot be verified with supported protocols`);
|
for (let i = 0; i < attempts; i++) {
|
||||||
obj_registry.setObjectStatus(obj.id, "skipped");
|
let result;
|
||||||
|
if (obj.c_id && obj.o_id) {
|
||||||
|
result = grpc_client.verifyHash(obj.c_id, obj.o_id, obj.payload_hash);
|
||||||
|
} else if (obj.s3_bucket && obj.s3_key) {
|
||||||
|
result = s3_client.verifyHash(obj.s3_bucket, obj.s3_key, obj.payload_hash);
|
||||||
|
} else {
|
||||||
|
console.log(`Object id=${obj.id} cannot be verified with supported protocols`);
|
||||||
|
return "skipped";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
return "verified";
|
||||||
|
} else if (result.error == "hash mismatch") {
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unless we explicitly saw that there was a hash mismatch, then we will retry after a delay
|
||||||
|
console.log(`Verify error on ${obj.id}: {resp.error}. Object will be re-tried`);
|
||||||
|
sleep(__ENV.SLEEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.success) {
|
return "invalid";
|
||||||
obj_registry.setObjectStatus(obj.id, "verified");
|
|
||||||
} else {
|
|
||||||
obj_registry.setObjectStatus(obj.id, "invalid");
|
|
||||||
console.log(`Verify error on ${obj.c_id}/${obj.o_id}: {resp.error}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue