TDengine/tests/army/create/test_stb_keep_compact.py

715 lines
No EOL
36 KiB
Python

###################################################################
# Copyright (c) 2016 by TAOS Technologies, Inc.
# All rights reserved.
#
# This file is proprietary and confidential to TAOS Technologies.
# No part of this file may be reproduced, stored, transmitted,
# disclosed or used in any form or by any means other than as
# expressly provided by the written permission from Jianhui Tao
#
###################################################################
# -*- coding: utf-8 -*-
import sys
import time
import datetime
import taos
import frame
import frame.etool
from frame.log import *
from frame.cases import *
from frame.sql import *
from frame.caseBase import *
from frame import *
class TDTestCase(TBase):
"""
Test case to verify super table keep parameter behavior with compaction
This test verifies that:
1. Super table keep parameter only takes effect during compaction
2. Before compaction, all historical data is visible regardless of keep settings
3. After compaction, data older than the keep period is removed
4. Different combinations of database keep and super table keep behave as expected
"""
def prepare_database_with_keep(self, db_name, db_keep):
"""Create a database with specified keep value"""
tdLog.info(f"Creating database {db_name} with keep={db_keep}")
tdSql.execute(f"DROP DATABASE IF EXISTS {db_name}")
# Ensure duration is small enough to satisfy the rule keep > 3*duration
# Calculate the minimum legal duration (ensure keep > 3*duration)
max_legal_duration = int(db_keep / 3)
if max_legal_duration < 1:
# If keep is too small, use smaller time unit
# Use hours as unit, 1 day = 24 hours
duration_hours = 12 # Use 12 hours
tdLog.info(f"Setting duration={duration_hours}h to ensure keep({db_keep}) > 3*duration")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION {duration_hours}h KEEP {db_keep}")
else:
duration = max(1, max_legal_duration - 1) # Conservatively, use smaller value, ensure it's an integer and not zero
tdLog.info(f"Setting duration={duration}d to ensure keep({db_keep}) > 3*duration")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION {duration}d KEEP {db_keep}")
tdSql.execute(f"USE {db_name}")
return True
def create_super_table_with_keep(self, stb_name, keep_days):
"""Create a super table with specified keep value in days"""
tdLog.info(f"Creating super table {stb_name} with keep={keep_days}d")
create_sql = f"CREATE STABLE {stb_name} (ts TIMESTAMP, val INT) TAGS (t_id INT) KEEP {keep_days}d"
tdSql.execute(create_sql)
return True
def create_tables_and_insert_data(self, stb_name, table_prefix, table_count=1):
"""Create child tables and insert data at different time points"""
# Current time and historical data points
now = int(time.time() * 1000) # Get current time directly, not relying on self
day1_ts = now - 1 * 24 * 3600 * 1000 # 1 day ago
day3_ts = now - 3 * 24 * 3600 * 1000 # 3 days ago
day5_ts = now - 5 * 24 * 3600 * 1000 # 5 days ago
day7_ts = now - 7 * 24 * 3600 * 1000 # 7 days ago
for i in range(1, table_count + 1):
tb_name = f"{table_prefix}_{i}"
tdLog.info(f"Creating child table {tb_name} under {stb_name}")
tdSql.execute(f"CREATE TABLE {tb_name} USING {stb_name} TAGS({i})")
# Insert data at different time points
tdLog.info(f"Inserting data into {tb_name} at different time points")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({now}, 100)")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day1_ts}, 90)")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day3_ts}, 70)")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day5_ts}, 50)")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day7_ts}, 30)")
# Log timestamps for reference
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now/1000))
day1_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day1_ts/1000))
day3_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day3_ts/1000))
day5_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day5_ts/1000))
day7_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day7_ts/1000))
tdLog.info(f"Inserted data at: Current ({current_time}), 1 day ago ({day1_time}), " +
f"3 days ago ({day3_time}), 5 days ago ({day5_time}), 7 days ago ({day7_time})")
return {
"now": now,
"day1_ts": day1_ts,
"day3_ts": day3_ts,
"day5_ts": day5_ts,
"day7_ts": day7_ts,
"day1_time": day1_time,
"day3_time": day3_time,
"day5_time": day5_time,
"day7_time": day7_time
}
def create_tables_and_insert_data_within_days(self, stb_name, table_prefix, max_days, table_count=1):
"""Create child tables and insert data at different time points within specified max days"""
# Current time and historical data points
now = int(time.time() * 1000) # Get current time directly, not relying on self
day1_ts = now - 1 * 24 * 3600 * 1000 # 1 day ago
day3_ts = now - 3 * 24 * 3600 * 1000 # 3 days ago
day5_ts = now - 5 * 24 * 3600 * 1000 # 5 days ago
for i in range(1, table_count + 1):
tb_name = f"{table_prefix}_{i}"
tdLog.info(f"Creating child table {tb_name} under {stb_name}")
tdSql.execute(f"CREATE TABLE {tb_name} USING {stb_name} TAGS({i})")
# Insert data at different time points within max_days
tdLog.info(f"Inserting data into {tb_name} at different time points within {max_days} days")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({now}, 100)")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day1_ts}, 90)")
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day3_ts}, 70)")
# Only insert 5-day old data if max_days >= 5
if max_days >= 5:
tdSql.execute(f"INSERT INTO {tb_name} VALUES ({day5_ts}, 50)")
# Log timestamps for reference
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now/1000))
day1_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day1_ts/1000))
day3_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day3_ts/1000))
day5_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day5_ts/1000))
tdLog.info(f"Inserted data at: Current ({current_time}), 1 day ago ({day1_time}), " +
f"3 days ago ({day3_time})" +
(f", 5 days ago ({day5_time})" if max_days >= 5 else ""))
return {
"now": now,
"day1_ts": day1_ts,
"day3_ts": day3_ts,
"day5_ts": day5_ts if max_days >= 5 else None,
"day1_time": day1_time,
"day3_time": day3_time,
"day5_time": day5_time if max_days >= 5 else None
}
def verify_data_count(self, table_name, expected_count, msg=""):
"""Verify the count of rows in a table"""
tdSql.query(f"SELECT COUNT(*) FROM {table_name}")
actual_count = tdSql.getData(0, 0)
tdLog.info(f"{msg} - Expected: {expected_count}, Actual: {actual_count}")
tdSql.checkEqual(actual_count, expected_count)
def verify_oldest_data(self, table_name, expected_val, msg=""):
"""Verify the oldest data value in a table"""
tdSql.query(f"SELECT val FROM {table_name} ORDER BY ts ASC LIMIT 1")
actual_val = tdSql.getData(0, 0)
tdLog.info(f"{msg} - Expected oldest value: {expected_val}, Actual: {actual_val}")
tdSql.checkEqual(actual_val, expected_val)
def trigger_compact(self, db_name):
"""Trigger database compaction"""
tdLog.info(f"Triggering compaction for database {db_name}...")
tdLog.info(f"FLUSH DATABASE {db_name}")
tdSql.execute(f"FLUSH DATABASE {db_name}")
time.sleep(5)
# Correct syntax includes database name
tdSql.execute(f"COMPACT DATABASE {db_name}")
# Wait for compaction to complete by checking status
while True:
tdSql.query(f"SHOW COMPACTS")
if tdSql.queryRows == 0:
break
time.sleep(1)
tdLog.info(f"Compaction operation for {db_name} completed")
def test_case1_stb_keep_2_db_keep_10(self):
"""Test case 1: STB keep=2, DB keep=10 - STB keep should determine data retention after compact"""
tdLog.info("=== Test Case 1: STB keep=2, DB keep=10 ===")
# Setup
self.prepare_database_with_keep("test_stb_compact1", 10)
self.create_super_table_with_keep("stb_keep2", 2)
self.create_tables_and_insert_data("stb_keep2", "tb_case1")
# Verify data before compact
self.verify_data_count("stb_keep2", 5, "Before compact - all data should be visible")
self.verify_oldest_data("stb_keep2", 30, "Before compact - 7-day old data should be visible")
# Trigger compact
self.trigger_compact("test_stb_compact1")
# Verify data after compact
# With STB keep=2, data older than 2 days should be removed
self.verify_data_count("stb_keep2", 2, "After compact - only data within 2 days should remain")
self.verify_oldest_data("stb_keep2", 90, "After compact - oldest data should be from 1 day ago")
def test_case2_stb_keep_6_db_keep_4(self):
"""Test case 2: STB keep=6, DB keep=4 - DB keep should override STB keep"""
tdLog.info("=== Test Case 2: STB keep=6, DB keep=4 ===")
# Setup
# Modify database keep value to 5, ensuring it satisfies keep > 3*duration rule
# Even with duration minimum of 1, keep=5 can satisfy the condition
db_keep = 5
self.prepare_database_with_keep("test_stb_compact2", db_keep)
self.create_super_table_with_keep("stb_keep6", 6)
# Only insert data within db_keep-1 days to avoid going beyond database keep range
safe_days = db_keep - 1 # Safe margin to avoid boundary condition issues
self.create_tables_and_insert_data_within_days("stb_keep6", "tb_case2", safe_days)
# Verify data before compact
# If safe_days=4, we only have at most 4 data points (current, 1 day ago, 3 days ago, not including 5 days ago)
expected_count = 3 # current, 1 day ago, 3 days ago
self.verify_data_count("stb_keep6", expected_count, "Before compact - all data should be visible")
self.verify_oldest_data("stb_keep6", 70, "Before compact - 3-day old data should be visible")
# Trigger compact
self.trigger_compact("test_stb_compact2")
# Verify data after compact
# Database keep=5, STB keep=6, all data is within retention range, so no data should be deleted
self.verify_data_count("stb_keep6", expected_count, "After compact - all data should remain")
self.verify_oldest_data("stb_keep6", 70, "After compact - oldest data should still be from 3 days ago")
def test_case3_multiple_stbs_with_different_keep(self):
"""Test case 3: Multiple STBs with different keep values in same database"""
tdLog.info("=== Test Case 3: Multiple STBs with different keep values ===")
# Setup
self.prepare_database_with_keep("test_stb_compact3", 10)
self.create_super_table_with_keep("stb_keep2", 2)
self.create_super_table_with_keep("stb_keep4", 4)
self.create_super_table_with_keep("stb_keep8", 8)
self.create_tables_and_insert_data("stb_keep2", "tb_keep2")
self.create_tables_and_insert_data("stb_keep4", "tb_keep4")
self.create_tables_and_insert_data("stb_keep8", "tb_keep8")
# Verify data before compact
for stb in ["stb_keep2", "stb_keep4", "stb_keep8"]:
self.verify_data_count(stb, 5, f"Before compact - all data should be visible in {stb}")
# Trigger compact
self.trigger_compact("test_stb_compact3")
# Verify data after compact
# Each STB should retain data according to its keep value
self.verify_data_count("stb_keep2", 2, "After compact - stb_keep2 should keep 2 days of data")
self.verify_oldest_data("stb_keep2", 90, "After compact - stb_keep2 oldest data from 1 day ago")
self.verify_data_count("stb_keep4", 3, "After compact - stb_keep4 should keep 4 days of data")
self.verify_oldest_data("stb_keep4", 70, "After compact - stb_keep4 oldest data from 3 days ago")
self.verify_data_count("stb_keep8", 5, "After compact - stb_keep8 should keep all data (within 8 days)")
self.verify_oldest_data("stb_keep8", 30, "After compact - stb_keep8 oldest data from 7 days ago")
def test_case4_boundary_keep_duration_ratio(self):
"""Test case 4: Testing boundary condition where keep is slightly above 3*duration"""
tdLog.info("=== Test Case 4: Boundary keep/duration ratio ===")
# Create a database with keep=10, duration=3 (exactly satisfying keep > 3*duration)
db_name = "test_stb_compact4"
db_keep = 10
duration = 3 # days
tdLog.info(f"Creating database with boundary condition: keep={db_keep}, duration={duration}d")
tdSql.execute(f"DROP DATABASE IF EXISTS {db_name}")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION {duration}d KEEP {db_keep}")
tdSql.execute(f"USE {db_name}")
# Create STB with keep<5 to test extreme condition
self.create_super_table_with_keep("stb_keep3", 3)
self.create_tables_and_insert_data("stb_keep3", "tb_boundary_min")
# Create STB with data
self.create_super_table_with_keep("stb_keep7", 7)
self.create_tables_and_insert_data("stb_keep7", "tb_boundary")
# Verify data before compact
self.verify_data_count("stb_keep7", 5, "Before compact - all data should be visible")
self.verify_oldest_data("stb_keep7", 30, "Before compact - 7-day old data should be visible")
self.verify_data_count("stb_keep3", 5, "Before compact - all data should be visible in stb_keep3")
# Trigger compact
self.trigger_compact("test_stb_compact4")
# Verify data after compact
# Database keep=10, STB keep=7, STB keep should determine retention
self.verify_data_count("stb_keep7", 4, "After compact - data within 7 days should remain")
self.verify_oldest_data("stb_keep7", 50, "After compact - oldest data from 7 days ago should remain")
# Verify minimum keep value STB (keep=3)
self.verify_data_count("stb_keep3", 2, "After compact - only data within 3 days should remain")
self.verify_oldest_data("stb_keep3", 90, "After compact - oldest data should be from 3 days ago")
def test_case5_write_time_with_keep_restrictions(self):
"""Test case 5: Testing write behavior with keep restrictions"""
tdLog.info("=== Test Case 5: Write behavior with keep restrictions ===")
# Setup: database keep=8, STB keep values 3 and 10
db_name = "test_stb_write_keep"
db_keep = 8
# Create database
tdLog.info(f"Creating database with keep={db_keep}")
tdSql.execute(f"DROP DATABASE IF EXISTS {db_name}")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION 2d KEEP {db_keep}")
tdSql.execute(f"USE {db_name}")
# Create two super tables: one with keep value less than database, one with greater
tdLog.info("Creating super tables with different keep values")
self.create_super_table_with_keep("stb_keep3", 3) # keep value less than database
self.create_super_table_with_keep("stb_keep10", 10) # keep value greater than database
# Create child tables
tdLog.info("Creating child tables")
tdSql.execute("CREATE TABLE tb_keep3_1 USING stb_keep3 TAGS(1)")
tdSql.execute("CREATE TABLE tb_keep10_1 USING stb_keep10 TAGS(1)")
# Get current time and historical timestamps
now = int(time.time() * 1000)
day6_ts = now - 6 * 24 * 3600 * 1000 # 6 days ago
day9_ts = now - 9 * 24 * 3600 * 1000 # 9 days ago
day6_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day6_ts/1000))
day9_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day9_ts/1000))
tdLog.info(f"Current time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now/1000))}")
tdLog.info(f"Time 6 days ago: {day6_time}")
tdLog.info(f"Time 9 days ago: {day9_time}")
# Scenario 1: Timestamp within database keep range (8d) but beyond STB keep range (3d)
tdLog.info("Scenario 1: Timestamp within database keep (8d) but beyond stable keep (3d)")
# Should be able to insert successfully
tdSql.execute(f"INSERT INTO tb_keep3_1 VALUES ({day6_ts}, 40)")
tdLog.info("Successfully inserted data beyond stable keep but within database keep - CORRECT BEHAVIOR")
# Check if data was inserted successfully
tdSql.query(f"SELECT * FROM tb_keep3_1 WHERE ts = {day6_ts}")
if tdSql.queryRows == 1:
tdLog.info("Verified data was inserted successfully")
else:
tdLog.info("ERROR: Failed to verify inserted data")
# Optional: Run compact to see if data beyond STB keep but within DB keep is removed
self.trigger_compact(db_name)
# Check if data still exists after compact
tdSql.query(f"SELECT * FROM tb_keep3_1 WHERE ts = {day6_ts}")
if tdSql.queryRows == 0:
tdLog.info("After compact: Data beyond STB keep (3d) was removed as expected")
else:
tdLog.info("After compact: Data beyond STB keep (3d) was retained (unexpected)")
# Scenario 2: Timestamp beyond database keep range (8d)
tdLog.info("Scenario 2: Timestamp beyond database keep (8d)")
expected_error = "Timestamp data out of range" # Expected error message
insert_sql = f"INSERT INTO tb_keep3_1 VALUES ({day9_ts}, 10)"
# Use tdSql.error to check if expected error is raised
try:
# This insertion should fail because it exceeds database keep range
tdSql.error(insert_sql)
tdLog.info("Insertion beyond database keep was correctly rejected - EXPECTED ERROR")
except Exception as e:
tdLog.info(f"ERROR: Expected error was not raised: {str(e)}")
# Don't raise exception, allow test to continue
# Scenario 3: Try to insert data beyond database keep into STB with keep value > database keep
tdLog.info("Scenario 3: Timestamp beyond database keep (8d) for table with stable keep (10d)")
insert_sql = f"INSERT INTO tb_keep10_1 VALUES ({day9_ts}, 10)"
# Use tdSql.error to check if expected error is raised
tdSql.error(insert_sql, expectErrInfo="Timestamp data out of range")
tdLog.info("Insertion beyond database keep for table with larger stable keep was successful - NEW EXPECTED BEHAVIOR")
def test_case6_db_keep_8_stb_keep_4(self):
"""Test case 6: DB keep=8, STB keep=4 - Testing data insertion before and after compaction"""
tdLog.info("=== Test Case 6: DB keep=8, STB keep=4 - Data insertion behavior ===")
# Setup
db_name = "test_stb_compact6"
db_keep = 8
stb_keep = 4
# Create database and super table
tdLog.info(f"Creating database with keep={db_keep}")
tdSql.execute(f"DROP DATABASE IF EXISTS {db_name}")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION 2d KEEP {db_keep}")
tdSql.execute(f"USE {db_name}")
# Create super table with keep=4
tdLog.info(f"Creating super table with keep={stb_keep}d")
self.create_super_table_with_keep("stb_keep4", stb_keep)
# Create child table
tdLog.info("Creating child table")
tdSql.execute("CREATE TABLE tb_keep4_1 USING stb_keep4 TAGS(1)")
# Get current time and historical timestamps
now = int(time.time() * 1000)
day3_ts = now - 3 * 24 * 3600 * 1000 # 3 days ago
day7_ts = now - 7 * 24 * 3600 * 1000 # 7 days ago
day3_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day3_ts/1000))
day7_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(day7_ts/1000))
tdLog.info(f"Current time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now/1000))}")
tdLog.info(f"Time 3 days ago: {day3_time}")
tdLog.info(f"Time 7 days ago: {day7_time}")
# Step 1: Insert data from 3 days ago and 7 days ago
tdLog.info("Step 1: Inserting data from 3 days ago and 7 days ago")
tdSql.execute(f"INSERT INTO tb_keep4_1 VALUES ({now}, 100)")
tdSql.execute(f"INSERT INTO tb_keep4_1 VALUES ({day3_ts}, 70)")
tdSql.execute(f"INSERT INTO tb_keep4_1 VALUES ({day7_ts}, 30)")
# Verify initial insertion
tdSql.query("SELECT COUNT(*) FROM stb_keep4")
tdLog.info(f"Initial data count: {tdSql.getData(0, 0)}")
tdSql.checkEqual(tdSql.getData(0, 0), 3)
# Step 2: Flush and compact the database
tdLog.info("Step 2: Flushing and compacting the database")
self.trigger_compact(db_name)
# Step 3: Query after compaction
tdLog.info("Step 3: Querying data after compaction")
tdSql.query("SELECT COUNT(*) FROM stb_keep4")
count_after_compact = tdSql.getData(0, 0)
tdLog.info(f"Data count after compaction: {count_after_compact}")
# Check if data from 7 days ago (beyond STB keep=4) is removed
tdSql.query(f"SELECT * FROM stb_keep4 WHERE ts = {day7_ts}")
if tdSql.queryRows == 0:
tdLog.info("Data from 7 days ago was correctly removed after compaction")
else:
tdLog.info("ERROR: Data from 7 days ago was not removed as expected")
# Check if data from 3 days ago (within STB keep=4) is retained
tdSql.query(f"SELECT * FROM stb_keep4 WHERE ts = {day3_ts}")
if tdSql.queryRows == 1:
tdLog.info("Data from 3 days ago was correctly retained after compaction")
else:
tdLog.info("ERROR: Data from 3 days ago was unexpectedly removed")
# Step 4: Try to insert data from 7 days ago again (after compaction)
tdLog.info("Step 4: Inserting data from 7 days ago after compaction")
tdSql.execute(f"INSERT INTO tb_keep4_1 VALUES ({day7_ts}, 35)")
# Verify new insertion
tdSql.query(f"SELECT * FROM stb_keep4 WHERE ts = {day7_ts}")
if tdSql.queryRows == 1:
tdLog.info("Successfully inserted data from 7 days ago after compaction")
tdLog.info(f"Value: {tdSql.getData(0, 1)}")
else:
tdLog.info("ERROR: Failed to insert data from 7 days ago after compaction")
# Get total count after new insertion
tdSql.query("SELECT COUNT(*) FROM stb_keep4")
count_after_insert = tdSql.getData(0, 0)
tdLog.info(f"Data count after new insertion: {count_after_insert}")
# Expected count: retained count from before + 1 new record
expected_count = count_after_compact + 1
tdSql.checkEqual(count_after_insert, expected_count)
def test_case7_alter_stb_keep(self):
"""Test case 7: Test ALTER STABLE KEEP parameter and its effect on data retention after compaction"""
tdLog.info("=== Test Case 7: ALTER STABLE KEEP parameter ===")
# Setup
db_name = "test_stb_alter_keep"
db_keep = 10 # Set database keep to a higher value to allow flexible STB keep testing
initial_stb_keep = 3 # Initial keep value
# Create database and super table with initial keep value
tdLog.info(f"Creating database with keep={db_keep}")
tdSql.execute(f"DROP DATABASE IF EXISTS {db_name}")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION 2d KEEP {db_keep}")
tdSql.execute(f"USE {db_name}")
# Create super table with initial keep
tdLog.info(f"Creating super table with initial keep={initial_stb_keep}d")
self.create_super_table_with_keep("stb_alter_keep", initial_stb_keep)
# Create child table and insert data with safer time margins
tdLog.info("Creating child table and inserting data")
# For safety, we'll insert data with specific values that are clearly within boundaries
now = int(time.time() * 1000) # Current time in milliseconds
# Add margin to ensure day calculations don't fall on boundary
# Subtract a few hours from each day boundary for safety
margin_hours = 4 # 4 hours safety margin
margin_ms = margin_hours * 3600 * 1000 # Convert to milliseconds
# Calculate timestamps with safety margins
day1_ts = now - (1 * 24 * 3600 * 1000) - margin_ms # ~1.2 days ago
day2_ts = now - (2 * 24 * 3600 * 1000) - margin_ms # ~2.2 days ago
day4_ts = now - (4 * 24 * 3600 * 1000) - margin_ms # ~4.2 days ago
day6_ts = now - (6 * 24 * 3600 * 1000) - margin_ms # ~6.2 days ago
# Create table and insert data
tdSql.execute("CREATE TABLE tb_alter_keep_1 USING stb_alter_keep TAGS(1)")
# Insert data at different time points
tdLog.info("Inserting data at different time points with safety margins")
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({now}, 100)") # Current
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day1_ts}, 90)") # ~1.2 days ago
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day2_ts}, 80)") # ~2.2 days ago
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day4_ts}, 60)") # ~4.2 days ago
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day6_ts}, 40)") # ~6.2 days ago
# Log the timestamps for debugging
tdLog.info(f"Current time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now/1000))}")
tdLog.info(f"~1.2 days ago: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(day1_ts/1000))}")
tdLog.info(f"~2.2 days ago: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(day2_ts/1000))}")
tdLog.info(f"~4.2 days ago: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(day4_ts/1000))}")
tdLog.info(f"~6.2 days ago: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(day6_ts/1000))}")
# Verify initial data insertion - all data should be visible
tdSql.query("SELECT COUNT(*) FROM stb_alter_keep")
initial_count = tdSql.getData(0, 0)
tdLog.info(f"Initial data count: {initial_count}")
tdSql.checkEqual(initial_count, 5)
# Create a timestamp map for later use
timestamps = {
"now": now,
"day1_ts": day1_ts,
"day2_ts": day2_ts,
"day4_ts": day4_ts,
"day6_ts": day6_ts,
}
# Perform first compaction with initial keep value
tdLog.info(f"Performing first compaction with STB keep={initial_stb_keep}")
self.trigger_compact(db_name)
# Verify data after first compaction - data older than initial_stb_keep should be removed
# With keep=3, and our safety margin, we expect data within ~2.9 days to be kept
# This should definitely include current, day1_ts, day2_ts, but not day4_ts or day6_ts
tdSql.query("SELECT COUNT(*) FROM stb_alter_keep")
count_after_first_compact = tdSql.getData(0, 0)
tdLog.info(f"Data count after first compaction: {count_after_first_compact}")
# Check individual records to see what was preserved
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {now}")
tdSql.checkEqual(tdSql.queryRows, 1)
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {day1_ts}")
tdSql.checkEqual(tdSql.queryRows, 1)
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {day2_ts}")
tdSql.checkEqual(tdSql.queryRows, 1)
tdSql.query(f"SELECT COUNT(*) FROM stb_alter_keep")
tdSql.checkEqual(tdSql.getData(0, 0), 3)
# Expected count should be the sum of records actually preserved
expected_preserved_count = 3
# Increase keep value
new_keep_value = 6 # Increase to 6 days
tdLog.info(f"Altering STB keep value from {initial_stb_keep} to {new_keep_value}")
tdSql.execute(f"ALTER STABLE stb_alter_keep KEEP {new_keep_value}d")
# Re-insert data at 4.2 days ago that was likely deleted in first compaction
tdLog.info("Re-inserting data from ~4.2 days ago")
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day4_ts}, 65)") # Different value to track
# Verify data after insertion but before second compaction
tdSql.query("SELECT COUNT(*) FROM stb_alter_keep")
count_after_reinsertion = tdSql.getData(0, 0)
tdLog.info(f"Data count after reinsertion: {count_after_reinsertion}")
expected_count_after_reinsertion = expected_preserved_count + 1 # +1 for the reinserted record
tdSql.checkEqual(count_after_reinsertion, expected_count_after_reinsertion)
# Perform second compaction with new keep value
tdLog.info(f"Performing second compaction with increased STB keep={new_keep_value}")
self.trigger_compact(db_name)
# Verify data after second compaction
tdSql.query("SELECT COUNT(*) FROM stb_alter_keep")
count_after_second_compact = tdSql.getData(0, 0)
tdLog.info(f"Data count after second compaction: {count_after_second_compact}")
tdSql.checkEqual(count_after_second_compact, 4)
# Verify the re-inserted data (day4) is retained after second compaction
tdSql.query(f"SELECT val FROM stb_alter_keep WHERE ts = {day4_ts}")
tdSql.checkEqual(tdSql.queryRows, 1)
tdSql.checkEqual(tdSql.getData(0, 0), 65)
# Check if day6 data was either never inserted or was correctly removed
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {day6_ts}")
tdSql.checkEqual(tdSql.queryRows, 0)
def test_case8_stb_keep_compact_with_db_keep(self):
"""Test case 8: Test STB keep compact with database keep"""
tdLog.info("=== Test Case 8: STB keep compact with database keep ===")
# Setup
db_name = "test_stb_alter_keep"
db_keep = 10 # Set database keep to a higher value to allow flexible STB keep testing
initial_stb_keep = 3 # Initial keep value
# Create database and super table with initial keep value
tdLog.info(f"Creating database with keep={db_keep}")
tdSql.execute(f"DROP DATABASE IF EXISTS {db_name}")
tdSql.execute(f"CREATE DATABASE {db_name} DURATION 2d KEEP {db_keep}")
tdSql.execute(f"USE {db_name}")
# Create super table with initial keep
tdLog.info(f"Creating super table with initial keep={initial_stb_keep}d")
self.create_super_table_with_keep("stb_alter_keep", initial_stb_keep)
# Create child table and insert data with safer time margins
tdLog.info("Creating child table and inserting data")
# For safety, we'll insert data with specific values that are clearly within boundaries
now = int(time.time() * 1000) # Current time in milliseconds
# Add margin to ensure day calculations don't fall on boundary
# Subtract a few hours from each day boundary for safety
margin_hours = 4 # 4 hours safety margin
margin_ms = margin_hours * 3600 * 1000 # Convert to milliseconds
# Calculate timestamps with safety margins
day0_ts = now - (0 * 24 * 3600 * 1000) - margin_ms # ~0.2 days ago
day1_ts = now - (1 * 24 * 3600 * 1000) - margin_ms # ~1.2 days ago
day2_ts = now - (2 * 24 * 3600 * 1000) - margin_ms # ~2.2 days ago
day4_ts = now - (4 * 24 * 3600 * 1000) - margin_ms # ~4.2 days ago
day6_ts = now - (6 * 24 * 3600 * 1000) - margin_ms # ~6.2 days ago
# Create table and insert data
tdSql.execute("CREATE TABLE tb_alter_keep_1 USING stb_alter_keep TAGS(1)")
# Insert data at different time points
tdLog.info("Inserting data at different time points with safety margins")
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({now}, 100)") # Current
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day1_ts}, 90)") # ~1.2 days ago
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day2_ts}, 80)") # ~2.2 days ago
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day4_ts}, 60)") # ~4.2 days ago
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day6_ts}, 40)") # ~6.2 days ago
# Decrease keep value to less than original
final_keep_value = 2 # Decrease to 2 days (less than original)
tdLog.info(f"Altering STB keep value from {initial_stb_keep} to {final_keep_value}")
tdSql.execute(f"ALTER STABLE stb_alter_keep KEEP {final_keep_value}d")
tdSql.execute(f"INSERT INTO tb_alter_keep_1 VALUES ({day0_ts}, 85)")
# Perform third compaction with reduced keep value
tdLog.info(f"Performing third compaction with decreased STB keep={final_keep_value}")
self.trigger_compact(db_name)
# Verify data after third compaction
tdSql.query("SELECT COUNT(*) FROM stb_alter_keep")
count_after_third_compact = tdSql.getData(0, 0)
tdLog.info(f"Data count after third compaction: {count_after_third_compact}")
# Check individual records to see what was preserved
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {now}")
tdSql.checkEqual(tdSql.queryRows, 1)
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {day0_ts}")
tdSql.checkEqual(tdSql.queryRows, 1)
tdSql.query(f"SELECT * FROM stb_alter_keep WHERE ts = {day1_ts}")
tdSql.checkEqual(tdSql.queryRows, 1)
# Expected count should be the sum of records actually preserved
expected_preserved_count = 3
tdSql.query(f"SELECT COUNT(*) FROM stb_alter_keep")
tdSql.checkEqual(tdSql.getData(0, 0), expected_preserved_count)
def run(self):
tdLog.debug(f"Start to execute {__file__}")
# 1. Test case 1: STB keep=2, DB keep=10
self.test_case1_stb_keep_2_db_keep_10()
# 2. Test case 2: STB keep=6, DB keep=4
self.test_case2_stb_keep_6_db_keep_4()
# 3. Test case 3: Multiple STBs with different keep values
self.test_case3_multiple_stbs_with_different_keep()
# 4. Test case 4: Boundary keep duration ratio
self.test_case4_boundary_keep_duration_ratio()
# 5. Test case 5: Write time with keep restrictions
self.test_case5_write_time_with_keep_restrictions()
# 6. Test case 6: DB keep=8, STB keep=4
self.test_case6_db_keep_8_stb_keep_4()
# 7. Test case 7: ALTER STABLE KEEP parameter
self.test_case7_alter_stb_keep()
# 8. Test case 8: STB keep compact with database keep
self.test_case8_stb_keep_compact_with_db_keep()
tdLog.success(f"{__file__} successfully executed")
tdCases.addLinux(__file__, TDTestCase())
tdCases.addWindows(__file__, TDTestCase())