Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion internal/infra/partition/bounds.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (r PartitionRange) IsEqual(r2 PartitionRange) bool {
return r.LowerBound.Equal(r2.LowerBound) && r.UpperBound.Equal(r2.UpperBound)
}

// Intersection returns the intersection between the intervals r1 and r2
// Intersection returns the intersection between the intervals r and r2
func (r PartitionRange) Intersection(r2 PartitionRange) PartitionRange {
var res PartitionRange // initialized with {time.Time{}, time.Time{}}

Expand Down
45 changes: 43 additions & 2 deletions scripts/bats/20_check.bats
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ load 'test/libs/partitions'
load 'test/libs/seeds'
load 'test/libs/sql'

setup_file() {
reset_database
}

setup() {
bats_load_library bats-support
bats_load_library bats-assert

reset_database
}

@test "Test exit code on PostgreSQL connection error" {
Expand Down Expand Up @@ -106,3 +108,42 @@ EOF
assert_failure
assert_output --partial "Found missing tables"
}

@test "Test check succeeding with an UUID partition key" {
local TABLE="test_uuid_1"
local INTERVAL=monthly
local RETENTION=2
local PREPROVISIONED=2

create_table_uuid_range ${TABLE}

declare -a PARTS=(
test_uuid_1_2024_12 01937f84-5800-7000-0000-000000000000 01941f29-7c00-7000-0000-000000000000
test_uuid_1_2025_01 01941f29-7c00-7000-0000-000000000000 0194bece-a000-7000-0000-000000000000
test_uuid_1_2025_02 0194bece-a000-7000-0000-000000000000 01954f00-b000-7000-0000-000000000000
test_uuid_1_2025_03 01954f00-b000-7000-0000-000000000000 0195eea5-d400-7000-0000-000000000000
test_uuid_1_2025_04 0195eea5-d400-7000-0000-000000000000 01968924-9c00-7000-0000-000000000000
)

create_partitions "$TABLE" "${PARTS[@]}"

local CONFIGURATION=$(cat << EOF
partitions:
unittest:
schema: public
table: ${TABLE}
interval: ${INTERVAL}
partitionKey: id
cleanupPolicy: drop
retention: ${RETENTION}
preProvisioned: ${PREPROVISIONED}
EOF
)
local CONFIGURATION_FILE=$(generate_configuration_file "${CONFIGURATION}")

PPM_WORK_DATE="2025-02-10" run postgresql-partition-manager run check -c ${CONFIGURATION_FILE}

assert_success
assert_output --partial "All partitions are correctly configured"
rm "$CONFIGURATION_FILE"
}
29 changes: 17 additions & 12 deletions scripts/bats/30_provisioning.bats
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ load 'test/libs/seeds'
load 'test/libs/sql'
load 'test/libs/time'

setup_file() {
reset_database
}

setup() {
bats_load_library bats-support
bats_load_library bats-assert

reset_database
}

@test "Test that provisioning succeed on up-to-date partitioning" {
Expand Down Expand Up @@ -315,7 +317,10 @@ EOF

@test "Test that provisioning continues after an error on a partition set" {
# Have a non-existing parent table, plus a normal set of partitions
local CONFIGURATION=$(cat << EOF

local TABLE=$(generate_table_name)

local CONFIGURATION=$(cat << EOF
partitions:
unittest1:
schema: public
Expand All @@ -327,7 +332,7 @@ partitions:
preProvisioned: 1
unittest2:
schema: public
table: table_unittest2
table: ${TABLE}
interval: daily
partitionKey: created_at
cleanupPolicy: drop
Expand All @@ -337,23 +342,23 @@ EOF
)
local CONFIGURATION_FILE=$(generate_configuration_file "${CONFIGURATION}")

create_partitioned_table "table_unittest2"
create_partitioned_table "${TABLE}"

PPM_WORK_DATE="2025-02-01" run postgresql-partition-manager run provisioning -c ${CONFIGURATION_FILE}

# The status must reflect the fact that one partition set failed
[ "$status" -eq 4 ] # PartitionsProvisioningFailedExitCode

# Check the success of the second set of partitions
local expected2=$(cat <<'EOF'
public|table_unittest2_2025_01_30|2025-01-30|2025-01-31
public|table_unittest2_2025_01_31|2025-01-31|2025-02-01
public|table_unittest2_2025_02_01|2025-02-01|2025-02-02
public|table_unittest2_2025_02_02|2025-02-02|2025-02-03
public|table_unittest2_2025_02_03|2025-02-03|2025-02-04
local expected2=$(cat <<EOF
public|${TABLE}_2025_01_30|2025-01-30|2025-01-31
public|${TABLE}_2025_01_31|2025-01-31|2025-02-01
public|${TABLE}_2025_02_01|2025-02-01|2025-02-02
public|${TABLE}_2025_02_02|2025-02-02|2025-02-03
public|${TABLE}_2025_02_03|2025-02-03|2025-02-04
EOF
)

run list_existing_partitions "unittest" "public" "table_unittest2"
run list_existing_partitions "unittest" "public" "${TABLE}"
assert_output "$expected2"
}
65 changes: 63 additions & 2 deletions scripts/bats/40_cleanup.bats
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ load 'test/libs/partitions'
load 'test/libs/seeds'
load 'test/libs/sql'

setup_file() {
reset_database
}

setup() {
bats_load_library bats-support
bats_load_library bats-assert

reset_database
}

@test "Test that useless partitions are removed by the cleanup" {
Expand Down Expand Up @@ -60,3 +62,62 @@ EOF
assert_table_not_exists public $(generate_daily_partition_name ${TABLE} ${i})
done
}

@test "Test that gaps in the partition set prevent any partition removal" {
local TABLE="test_uuid_gap"
local INTERVAL=monthly
local RETENTION=1
local PREPROVISIONED=2

create_table_uuid_range ${TABLE}

declare -a PARTS=(
${TABLE}_2024_12 01937f84-5800-7000-0000-000000000000 01941f29-7c00-7000-0000-000000000000
# next partition is missing
# ${TABLE}_2025_01 01941f29-7c00-7000-0000-000000000000 0194bece-a000-7000-0000-000000000000
${TABLE}_2025_02 0194bece-a000-7000-0000-000000000000 01954f00-b000-7000-0000-000000000000
${TABLE}_2025_03 01954f00-b000-7000-0000-000000000000 0195eea5-d400-7000-0000-000000000000
${TABLE}_2025_04 0195eea5-d400-7000-0000-000000000000 01968924-9c00-7000-0000-000000000000
)

create_partitions "$TABLE" "${PARTS[@]}"

local CONFIGURATION=$(cat << EOF
partitions:
unittest:
schema: public
table: ${TABLE}
interval: ${INTERVAL}
partitionKey: id
cleanupPolicy: drop
retention: ${RETENTION}
preProvisioned: ${PREPROVISIONED}
EOF
)
local CONFIGURATION_FILE=$(generate_configuration_file "${CONFIGURATION}")

# When run on 2025-03-15 with a retention of 1 month, the partition for 2024-12
# is old enough to be dropped. But since 2025-01 is missing, it is an error that
# should prevent the drop.
PPM_WORK_DATE="2025-03-15" run postgresql-partition-manager run cleanup -c ${CONFIGURATION_FILE}

assert_failure
assert_output --partial 'level=ERROR msg="Partition Gap"'

# Check that all the partitions are still there
local i=0
local expected=""
while (( i < ${#PARTS[*]} ))
do
# bats does not expect any trailing newline, so append it to each previous line
# except the first
if (( i > 0 )); then expected+=$'\n'; fi
expected+="public|${PARTS[i]}|${PARTS[i+1]}|${PARTS[i+2]}"
(( i+=3 ))
done
run list_existing_partitions "unittest" "public" "${TABLE}"

assert_output "$expected"

rm "$CONFIGURATION_FILE"
}
33 changes: 33 additions & 0 deletions scripts/bats/test/libs/partitions.bash
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,36 @@ generate_daily_partition_name() {
generate_table_name() {
cat /dev/urandom | head -n 1 | base64 | tr -dc '[:alnum:]' | tr '[:upper:]' '[:lower:]' | cut -c -13 | sed -e 's/^[0-9]/a/g'
}

# Generic method to create child partitions with specified ranges
# Arguments: parent table and an array of partition definitions
# The array elements are:
# [0] partition name
# [1] lower bound
# [2] upper bound
# [3] name of next partition
# [4] lower bound for next partition
# ...and so on (3 elements per partition)
create_partitions() {
local tbl="$1"
shift
local parts=("$@")

local len=${#parts[*]}
if ! (( len % 3 == 0 )); then
echo >&2 "The list must have 3 elements per partition (found length=$len)"
return 1
fi
i=0
local sql_block="BEGIN;"
while (( i < len ))
do
# Partition names and ranges must be SQL-quoted by the caller if needed
sql_block="$sql_block
CREATE TABLE ${parts[i]} PARTITION OF ${tbl} FOR VALUES FROM ('${parts[((i+1))]}') TO ('${parts[((i+2))]}') ;"
(( i+=3 ))
done
sql_block="$sql_block
COMMIT;";
execute_sql_commands "$sql_block"
}
13 changes: 13 additions & 0 deletions scripts/bats/test/libs/seeds.bash
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ EOQ
execute_sql "${QUERY}"
}

create_table_uuid_range() {
local TABLE="$1"

read -r -d '' QUERY <<EOQ ||
CREATE TABLE ${TABLE} (
id uuid,
value INT,
created_at DATE NOT NULL
) PARTITION BY RANGE (id);
EOQ
execute_sql "${QUERY}"
}

generate_configuration_file() {
local PARTITION_CONFIGURATION=$1
local CONFIGURATION_TEMPLATE_FILE=configuration/template.yaml
Expand Down
9 changes: 4 additions & 5 deletions scripts/bats/test/libs/sql.bash
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ execute_sql_file() {

# Execute multiple semicolon-separated commands
execute_sql_commands() {
local SQL_COMMANDS=$1
local DATABASE=$2
# $1: sql commands
# $2: dbname (if unset, psql defaults to $PGDATABASE and then $USER)

PGDATABASE="$DATABASE" psql --tuples-only --no-align --quiet <<EOSQL
$SQL_COMMANDS
;
psql --tuples-only --no-align --quiet "$2" <<EOSQL
$1
EOSQL
}

Expand Down
Loading