Saturday, August 30, 2025

Direct Execution:

SET SERVEROUTPUT ON;

-- Step 1: Drop and recreate tables to ensure correct structure
BEGIN
  EXECUTE IMMEDIATE 'DROP TABLE tablespace_control';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -942 THEN NULL;
    ELSE RAISE;
    END IF;
END;
/

BEGIN
  EXECUTE IMMEDIATE 'DROP TABLE tablespace_log';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -942 THEN NULL;
    ELSE RAISE;
    END IF;
END;
/

BEGIN
  EXECUTE IMMEDIATE q'[
    CREATE TABLE tablespace_control (
      tablespace_name VARCHAR2(30) PRIMARY KEY,
      initial_size_gb NUMBER NOT NULL,
      status VARCHAR2(10) DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED')),
      retry_count NUMBER DEFAULT 0,
      last_attempt TIMESTAMP,
      created_by VARCHAR2(30) DEFAULT USER,
      CONSTRAINT chk_size_positive CHECK (initial_size_gb > 0)
    )
  ]';
END;
/

BEGIN
  EXECUTE IMMEDIATE q'[
    CREATE TABLE tablespace_log (
      log_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
      tablespace_name VARCHAR2(30),
      action VARCHAR2(20),
      status VARCHAR2(10),
      error_message VARCHAR2(4000),
      log_time TIMESTAMP DEFAULT SYSTIMESTAMP,
      session_user VARCHAR2(30) DEFAULT USER,
      session_id NUMBER DEFAULT SYS_CONTEXT('USERENV', 'SID')
    )
  ]';
END;
/

-- Step 2: Populate tablespace_control (replace with your 64 tablespaces)
BEGIN
  INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TABLESPACE1', 6268);
  INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TABLESPACE2', 3000);
  INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TABLESPACE3', 2500);
  -- Add remaining 61 tablespaces (e.g., from parse_tablespace_script.sql output)
  -- Example loop for testing (comment out after replacing):
  /*
  FOR i IN 4..64 LOOP
    INSERT INTO tablespace_control (tablespace_name, initial_size_gb)
    VALUES ('TABLESPACE' || i, CASE WHEN MOD(i, 2) = 0 THEN 3000 ELSE 2000 END);
  END LOOP;
  */
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error populating tablespace_control: ' || SQLERRM);
    RAISE;
END;
/

-- Step 3: Create tablespaces directly
DECLARE
  v_common_clause CONSTANT VARCHAR2(500) := 'AUTOEXTEND ON NEXT 1G MAXSIZE UNLIMITED LOGGING DEFAULT NO INMEMORY ONLINE EXTENT MANAGEMENT LOCAL AUTOALLOCATE BLOCKSIZE 8K SEGMENT SPACE MANAGEMENT AUTO FLASHBACK ON';
  v_max_retries CONSTANT NUMBER := 3;
  v_batch_size CONSTANT NUMBER := 3; -- Smaller batch
  v_processed NUMBER := 0;
  v_sql VARCHAR2(1000);
  v_error_msg VARCHAR2(4000);
BEGIN
  DBMS_OUTPUT.ENABLE(1000000);
  FOR rec IN (
    SELECT tablespace_name, initial_size_gb, retry_count
    FROM tablespace_control
    WHERE status IN ('PENDING', 'FAILED')
    ORDER BY tablespace_name
  ) LOOP
    DBMS_OUTPUT.PUT_LINE('Processing: ' || rec.tablespace_name || ', Size: ' || rec.initial_size_gb || ', Retry: ' || rec.retry_count);

    -- Check if tablespace exists
    DECLARE
      v_exists NUMBER;
    BEGIN
      SELECT COUNT(*) INTO v_exists FROM DBA_TABLESPACES WHERE tablespace_name = rec.tablespace_name;
      IF v_exists > 0 THEN
        DBMS_OUTPUT.PUT_LINE('Inserting SKIP_EXISTS log for: ' || rec.tablespace_name);
        UPDATE tablespace_control SET status = 'COMPLETED', last_attempt = SYSTIMESTAMP
        WHERE tablespace_name = rec.tablespace_name;
        INSERT INTO tablespace_log (tablespace_name, action, status)
        VALUES (rec.tablespace_name, 'SKIP_EXISTS', 'COMPLETED');
        COMMIT;
        DBMS_OUTPUT.PUT_LINE('Skipped: ' || rec.tablespace_name || ' (already exists)');
        CONTINUE;
      END IF;
    END;

    -- Mark as RUNNING
    DBMS_OUTPUT.PUT_LINE('Inserting RUNNING status for: ' || rec.tablespace_name);
    UPDATE tablespace_control SET status = 'RUNNING', last_attempt = SYSTIMESTAMP
    WHERE tablespace_name = rec.tablespace_name;
    COMMIT;

    -- Create tablespace
    v_sql := 'CREATE BIGFILE TABLESPACE ' || DBMS_ASSERT.SQL_OBJECT_NAME(rec.tablespace_name) || ' DATAFILE SIZE ' || rec.initial_size_gb || 'G ' || v_common_clause;
    BEGIN
      DBMS_OUTPUT.PUT_LINE('Executing: ' || SUBSTR(v_sql, 1, 200) || '...');
      EXECUTE IMMEDIATE v_sql;
      DBMS_OUTPUT.PUT_LINE('Inserting COMPLETED log for: ' || rec.tablespace_name);
      UPDATE tablespace_control SET status = 'COMPLETED', retry_count = 0, last_attempt = SYSTIMESTAMP
      WHERE tablespace_name = rec.tablespace_name;
      INSERT INTO tablespace_log (tablespace_name, action, status)
      VALUES (rec.tablespace_name, 'CREATE', 'COMPLETED');
      COMMIT;
      DBMS_OUTPUT.PUT_LINE('Created: ' || rec.tablespace_name);
    EXCEPTION
      WHEN OTHERS THEN
        v_error_msg := SQLERRM;
        DBMS_OUTPUT.PUT_LINE('Inserting FAILED log for: ' || rec.tablespace_name);
        UPDATE tablespace_control SET status = 'FAILED', retry_count = retry_count + 1, last_attempt = SYSTIMESTAMP
        WHERE tablespace_name = rec.tablespace_name;
        INSERT INTO tablespace_log (tablespace_name, action, status, error_message)
        VALUES (rec.tablespace_name, 'CREATE', 'FAILED', v_error_msg);
        COMMIT;
        DBMS_OUTPUT.PUT_LINE('Failed: ' || rec.tablespace_name || ' - ' || v_error_msg);
        IF rec.retry_count + 1 < v_max_retries THEN
          DBMS_OUTPUT.PUT_LINE('Inserting AUTO_RETRY log for: ' || rec.tablespace_name);
          INSERT INTO tablespace_log (tablespace_name, action, status)
          VALUES (rec.tablespace_name, 'AUTO_RETRY', 'SCHEDULED');
          COMMIT;
          DBMS_OUTPUT.PUT_LINE('Scheduled retry for: ' || rec.tablespace_name);
        END IF;
    END;

    -- Batch pause
    v_processed := v_processed + 1;
    IF MOD(v_processed, v_batch_size) = 0 THEN
      DBMS_OUTPUT.PUT_LINE('Pausing for 180 seconds after batch of ' || v_batch_size);
      DBMS_LOCK.SLEEP(180);
    END IF;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('Tablespace creation complete. Check tablespace_log for details.');
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error in main loop: ' || SQLERRM);
END;
/

How to Use

Populate tablespace_control:
  • Use the output from parse_tablespace_script.sql (artifact ID a903bb09-da7e-470a-a396-623c177a7737) to insert your 64 tablespaces:
    sql
    INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TABLESPACE1', 6268);
    INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TABLESPACE2', 3000);
    -- Add all 64
    COMMIT;
  • Verify:
    sql
    SELECT COUNT(*) FROM tablespace_control;
  1. Run the Script:
    • In TOAD SQL Editor, execute the script with SET SERVEROUTPUT ON;.
    • Output shows progress (e.g., Created: TABLESPACE1, Skipped: TABLESPACE2 (already exists), Failed: TABLESPACE3 - ORA-01658).
    • The script skips existing tablespaces (SKIP_EXISTS), retries failures (3 times), and logs to tablespace_log.
  2. Mitigate TOAD Timeout:
    • Batch size is reduced to 5 (v_batch_size = 5) with 120-second pauses (DBMS_LOCK.SLEEP(120)) to minimize session timeout risk.
    • In TOAD, increase session timeout if possible (check “Database” > “Session” settings).
    • If timeout occurs, re-run the script—it resumes from PENDING or FAILED tablespaces.
  3. Monitor:
    • Check logs:
      sql
      SELECT log_id, tablespace_name, action, status, error_message, log_time
      FROM tablespace_log
      WHERE action IN ('CREATE', 'SKIP_EXISTS', 'AUTO_RETRY')
      ORDER BY log_time DESC;
    • Check status:
      sql
      SELECT tablespace_name, initial_size_gb, status, retry_count, last_attempt
      FROM tablespace_control
      ORDER BY tablespace_name;
    • Verify tablespaces:
      sql
      SELECT tablespace_name, bigfile, ROUND(bytes/1024/1024/1024, 2) AS size_gb
      FROM dba_tablespaces
      WHERE tablespace_name LIKE 'TABLESPACE%'
      ORDER BY tablespace_name;
  4. Retry Failures:
    • Reset failed tablespaces:
      sql
      UPDATE tablespace_control SET status = 'PENDING', retry_count = 0 WHERE status = 'FAILED';
      COMMIT;
    • Re-run the script.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

How to Use in Your Workflow

  1. Backup tablespace_control (Optional):
    • Before deleting, save the current rows to restore later:
      sql
      CREATE TABLE tablespace_control_backup AS SELECT * FROM tablespace_control;
    • Or save INSERT statements:
      sql
      SELECT 'INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES (''' || tablespace_name || ''', ' || initial_size_gb || ');'
      FROM tablespace_control;
  2. Run the DELETE:
    • Modify the DELETE statement with your desired tablespaces (e.g., first 10):
      sql
      DELETE FROM tablespace_control WHERE tablespace_name NOT IN ('TABLESPACE1', 'TABLESPACE2', 'TABLESPACE3', ..., 'TABLESPACE10');
      COMMIT;
    • Verify:
      sql
      SELECT tablespace_name, initial_size_gb, status FROM tablespace_control;
  3. Run the Direct Execution Script:
    • Execute create_multiple_tablespaces_direct.sql (version dc078ecc-90db-4e89-9e90-306d80616933) in TOAD with SET SERVEROUTPUT ON;.
    • The script processes only the remaining tablespaces, skipping existing ones and logging SKIP_EXISTS.
  4. Monitor:
    • Check logs:
      sql
      SELECT log_id, tablespace_name, action, status, error_message, log_time
      FROM tablespace_log
      WHERE action IN ('CREATE', 'SKIP_EXISTS', 'AUTO_RETRY')
      ORDER BY log_time DESC;
    • Check status:
      sql
      SELECT tablespace_name, initial_size_gb, status, retry_count
      FROM tablespace_control;
  5. Add More Tablespaces:
    • Repopulate tablespace_control for the next batch:
      sql
      INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TABLESPACE11', 2000);
      -- Add next batch
      COMMIT;
    • Re-run the script.

RDS-Specific Notes

  • Timeout Risk: The direct script runs synchronously, so TOAD’s 1-hour timeout may interrupt large batches (e.g., 6268G tablespaces). The DELETE reduces this risk by limiting to 10 tablespaces. Adjust v_batch_size = 3and DBMS_LOCK.SLEEP(180) if needed.
  • Storage: 10 tablespaces at ~3000G (~30 TB) is still significant. Check:
    sql
    SELECT SUM(bytes)/1024/1024/1024 AS total_gb FROM dba_data_files;
    Scale storage via AWS console if needed (max 64 TiB).
  • Privileges: Ensure:
    sql
    GRANT CREATE TABLESPACE, CREATE TABLE TO your_username;

My Opinion

  • Why DELETE Helps: Limiting to 10 tablespaces via DELETE is a practical way to manage TOAD timeouts while job_queue_processes = 0 awaits AWS Support resolution. It allows incremental progress without losing the skip-existing (SKIP_EXISTS) or retry functionality.
  • Long-Term: Contact AWS Support to set job_queue_processes > 0 (e.g., 100) for true background execution using the original Scheduler script.
  • Testing: Start with a small batch (e.g., 3 tablespaces at 1G) to confirm the script works:
    sql
    TRUNCATE TABLE tablespace_control;
    INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TEST_TS_01', 1);
    INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TEST_TS_02', 1);
    INSERT INTO tablespace_control (tablespace_name, initial_size_gb) VALUES ('TEST_TS_03', 1);
    COMMIT;

If you provide your 64 tablespace names/sizes, I can generate the exact INSERT and DELETE statements for your batches!

No comments: