Skip to content

Commit

Permalink
page iterator (LPI): add integration test
Browse files Browse the repository at this point in the history
* add `ais/test/scripts/remais-del-list-deleted.sh`
* remais for out-band delete
  - ais backend: fix remote lsmsg flags
* part seven, prev. commit: 6c97b68

Signed-off-by: Alex Aizman <[email protected]>
  • Loading branch information
alex-aizman committed Jan 24, 2025
1 parent 6c97b68 commit acda3ec
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 45 deletions.
1 change: 1 addition & 0 deletions ais/backend/ais.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ func (m *AISbp) ListObjects(remoteBck *meta.Bck, msg *apc.LsoMsg, lst *cmn.LsoRe
}
remoteMsg := msg.Clone()
remoteMsg.PageSize = calcPageSize(remoteMsg.PageSize, remoteBck.MaxPageSize())
remoteMsg.ClearFlag(apc.LsVerChanged | apc.LsObjCached)

// TODO:
// Currently, not encoding xaction (aka request) `UUID` from the remote cluster
Expand Down
101 changes: 96 additions & 5 deletions ais/test/scripted_cli_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// Package integration_test.
/*
* Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
*/
package integration_test

import (
"os"
"os/exec"
"strconv"
"strings"
"testing"

"github.com/NVIDIA/aistore/api"
"github.com/NVIDIA/aistore/api/apc"
"github.com/NVIDIA/aistore/cmn"
"github.com/NVIDIA/aistore/cmn/cos"
Expand Down Expand Up @@ -119,7 +121,7 @@ func TestPrefetchLatestRemaisUsingScript(t *testing.T) {
tassert.CheckFatal(t, err)
}

func TestCopySyncWithOutOfBandUsingRemaisScript(t *testing.T) {
func TestCopySyncRemaisUsingScript(t *testing.T) {
bck := cliBck
tools.CheckSkip(t, &tools.SkipTestArgs{
Bck: bck,
Expand All @@ -144,7 +146,7 @@ func TestCopySyncWithOutOfBandUsingRemaisScript(t *testing.T) {
// instead, using aisore S3 API with a temp `ais://` bucket, and with two additional workarounds:
// 1. MD5
// 2. "apc.S3Scheme+apc.BckProviderSeparator+bck.Name" (below)
func TestMultipartUploadLargeFilesScript(t *testing.T) {
func TestMultipartUploadUsingScript(t *testing.T) {
tools.CheckSkip(t, &tools.SkipTestArgs{
Long: true,
})
Expand Down Expand Up @@ -179,7 +181,7 @@ func TestMultipartUploadLargeFilesScript(t *testing.T) {
}

// remais-blob-download.sh
func TestRemaisBlobDownloadScript(t *testing.T) {
func TestRemaisBlobDownloadUsingScript(t *testing.T) {
tools.CheckSkip(t, &tools.SkipTestArgs{
RequiresRemoteCluster: true,
Long: true,
Expand Down Expand Up @@ -209,7 +211,7 @@ func TestRemaisBlobDownloadScript(t *testing.T) {
}

// get-archregx-wdskey.sh
func TestGetArchregxWdskeyScript(t *testing.T) {
func TestGetArchregxWdskeyUsingScript(t *testing.T) {
cmd := exec.Command("./scripts/get-archregx-wdskey.sh")

out, err := cmd.CombinedOutput()
Expand All @@ -218,3 +220,92 @@ func TestGetArchregxWdskeyScript(t *testing.T) {
}
tassert.CheckFatal(t, err)
}

// runs 2 scripts to generate test subtree and then delete random bunch
// see related unit: fs/lpi_test
// TODO -- FIXME: assert (scnt == lcnt) once it passes a few times
func TestRemaisDeleteUsingScript(t *testing.T) {
tools.CheckSkip(t, &tools.SkipTestArgs{
RequiresRemoteCluster: true,
Long: true,
})

const (
lpiGenScript = "./scripts/gen-nested-dirs.sh"
lpiGenPrefix = "Total files created: "

remaisScript = "./scripts/remais-del-list-deleted.sh"
)
// 1. create temp root
root, err := os.MkdirTemp("", "ais-lpi-")
tassert.CheckFatal(t, err)
defer func() {
if !t.Failed() {
os.RemoveAll(root)
}
}()

//
// 2. script #1: generate
//
cmd := exec.Command(lpiGenScript, "--root_dir", root)
out, err := cmd.CombinedOutput()
tassert.CheckFatal(t, err)

var (
lines = strings.Split(string(out), "\n")
num int64
)
for _, ln := range lines {
i := strings.Index(ln, lpiGenPrefix)
if i >= 0 {
s := ln[i+len(lpiGenPrefix):]
num, err = strconv.ParseInt(s, 10, 64)
tassert.CheckFatal(t, err)
}
}
tassert.Fatalf(t, num > 0, "failed to parse script output (num %d)", num)

//
// 3. script #2: put generated subtree => remais and perform random out-of-band delete
//
bck := cmn.Bck{
Name: trand.String(10),
Ns: cmn.Ns{UUID: tools.RemoteCluster.Alias},
Provider: apc.AIS,
}
tools.CreateBucket(t, proxyURL, bck, nil, false /*cleanup*/)
name := bck.Cname("")
tlog.Logln("bucket " + name)

cmd = exec.Command(remaisScript, "--root_dir", root, "--bucket", name)
out, err = cmd.CombinedOutput()
// tlog.Logln(string(out))
tassert.CheckFatal(t, err)

lines = strings.Split(string(out), "\n")
var scnt int
for _, ln := range lines {
if strings.HasPrefix(ln, "deleted:") {
scnt++
}
}
tlog.Logf("## out-of-band deleted objects:\t%d\n", scnt)

// 4. ls --check-versions
lsmsg := &apc.LsoMsg{}
lsmsg.AddProps([]string{apc.GetPropsName, apc.GetPropsSize, apc.GetPropsVersion, apc.GetPropsCopies,
apc.GetPropsLocation, apc.GetPropsCustom}...)
lsmsg.SetFlag(apc.LsVerChanged)
lst, err := api.ListObjects(baseParams, bck, lsmsg, api.ListArgs{})
tassert.CheckFatal(t, err)

var lcnt int
for _, en := range lst.Entries {
if en.IsAnyFlagSet(apc.EntryVerRemoved) {
// fmt.Println("\tdeleted:", en.Name)
lcnt++
}
}
tlog.Logf("## list-objects version removed:\t%d\n", lcnt)
}
36 changes: 0 additions & 36 deletions ais/test/scripts/del_nested_files.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash

# =============================================================================
# Script: gen_nested_dirs.sh
# Script: gen-nested-dirs.sh
# Description: Generates a nested directory structure with randomly named zero-size files.
# Usage: ./gen_nested_dirs.sh [OPTIONS]
# Usage: ./gen-nested-dirs.sh [OPTIONS]
#
# Options:
# --root_dir DIR Root directory for test environment (default: /tmp/ais-<PID>-test)
Expand All @@ -14,7 +14,7 @@
# --help, -h Display this help message
#
# See related:
# - ais/test/scripts/del_nested_files.sh
# - ais/test/scripts/remais-del-list-deleted.sh
# =============================================================================

# Defaults
Expand Down
105 changes: 105 additions & 0 deletions ais/test/scripts/remais-del-list-deleted.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/bash

# Randomly delete 10% of the generated files
#
# See related:
# - ais/test/scripts/gen-nested-dirs.sh

# Defaults
root_dir="/tmp/abc"
bucket="ais://@remais/abc"

# usage
usage() {
echo "Usage: $0 [OPTIONS]"
echo
echo "Options:"
echo " --root_dir DIR Root directory containing generated test files"
echo " --bucket BUCKET Bucket in a remote cluster"
echo " -h, --help Display this help message"
exit 1
}

## command line
while (( "$#" )); do
case "${1}" in
--root_dir) root_dir=$2; shift; shift;;
--bucket) bucket=$2; shift; shift;;
-h|--help) usage; exit 0;;
*) echo "fatal: unknown argument '${1}'"; exit 1;;
esac
done
if [[ "$root_dir" != /tmp/* ]]; then
echo "Error: expecting root directory to be under /tmp, got: $root_dir"
exit 1
fi
if [ ! -d "$root_dir" ]; then
echo "Error: root '$root_dir' does not exist"
exit 1
fi

## must be @remais
[[ "$bucket" == *//@* ]] || { echo "Error: expecting remote ais bucket, got ${bucket}"; exit 1; }

all_files=( $(find "$root_dir" -type f) )
total_files=${#all_files[@]}

## remais must be attached
rendpoint=$(ais show remote-cluster -H | awk '{print $2}')
[[ ! -z "$rendpoint" ]] || { echo "Error: no remote ais clusters"; exit 1; }
uuid=$(ais show remote-cluster -H | awk '{print $1}')

## actual bucket inside remais:
rbucket="ais://$(basename ${bucket})"

echo "Note: remote ais bucket $bucket is, in fact, ais://@${uuid}/$(basename ${bucket})"
echo

## check remote bucket; create if doesn't exist
exists=true
ais show bucket $bucket -c 1>/dev/null 2>&1 || exists=false
if [[ "$exists" == "false" ]]; then
ais create $bucket
fi

cleanup() {
rc=$?
if [[ "$exists" == "false" ]]; then
ais rmb $bucket -y 1> /dev/null 2>&1
fi
exit $rc
}

trap cleanup EXIT INT TERM

## 2.put generated subtree => remais
AIS_ENDPOINT=$rendpoint ais put $root_dir $rbucket --yes --recursive
ais prefetch $bucket --wait

ais ls $bucket --cached --summary || exit $?
AIS_ENDPOINT=$rendpoint ais ls $rbucket --summary || exit $?

## 3. delete out-of-band
num_files_to_delete=$((total_files / 10))
deleted_files=()
for ((i=0; i<num_files_to_delete; i++)); do
random_index=$((RANDOM % total_files))
file_to_delete=${all_files[$random_index]}

relative_path="${file_to_delete#$root_dir/}"
deleted_obj="$rbucket/$relative_path"

AIS_ENDPOINT=$rendpoint ais rmo $deleted_obj 1> /dev/null || exit $?

deleted_objs+=("$deleted_obj")

# remove deleted file from array
all_files=("${all_files[@]:0:$random_index}" "${all_files[@]:$((random_index + 1))}")
total_files=${#all_files[@]}
done

# Return the deleted pathnames
# echo "Deleted objects:"
for file in "${deleted_objs[@]}"; do
echo "deleted: $file"
done
4 changes: 3 additions & 1 deletion fs/lpi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import (
"github.com/NVIDIA/aistore/tools/tassert"
)

// compare w/ namesake integration in ais/test/scripted_cli_test.go

const (
lpiTestPrefix = "Total files created: "
lpiTestScript = "../ais/test/scripts/gen_nested_dirs.sh"
lpiTestScript = "../ais/test/scripts/gen-nested-dirs.sh"
lpiTestPageSize = 10
lpiTestVerbose = false
)
Expand Down

0 comments on commit acda3ec

Please sign in to comment.