EazyTMS AO nightly daily database update
Introduction
EazyTMS development needs fresh AO data.
We have implemented and deployed daily database restore from Ahola AO Live to EasyTMS AO nightly.
* EasyTMS AO nightly has own environment which includes Daily updated database, client, server and service.
* Developers can access to database with Rest Servers and Client.
* Environment is on bckendrsvtst-0:
\\bckendsrvtst-0\EazyTMS\
Steps
Daily updated database copy from Live
1. Scheduled export from Live
2. backup.bak transfer form stable bucket to nightly bucket
3. Delete old EazyTMS_AO_nightly database
4. import to nightly SQL Instance
5. set db user with correct permissions → SQL Agent
6. deletion processes to bucket and instance databases
7. Zabbix monitoring atleast for logs to Grafana
1. Database Export - Import logs
~~Database can be same version as live, so we dont need to ran evolution to db on every deployment~~ We have automated
evolution_main.sqlwith SQL Agent.
Scheduling
timing is different between summer | winter time. So utc+3 and utc+2
scheduling is marked as summer time utc+3
1. klo 03:00 stop servers, service, start db export 2. klo 03:00, after successfull export → transfer .bak between buckets 3. klo 04:00 delete existing eazytms db 4. klo after successfull databse deletion process → import db 5. klo 04:45 add db user to new db 6. klo 04:47 evolution_main.sql 7. klo 04:48 Upgrade compatibility level 7. klo 04:49 start service 8. klo 4:55 start servers
STABLE:
We will run the job on
Teleport serverwith scheduled bash scripts.
Teleport -server (10.170.0.67) is in common-servers-01 -project
Bash script to export and transfer backup scheduled cron job:
* Export db as a backup from production (Ahola_AO_Live)
* Moving backup from stable bucket to nightly bucket
/home/jaani/script/export_db.sh
#!/bin/bash
# Define variables
INSTANCE_NAME="mssql-stable"
BUCKET_PATH="gs://ao_db_live/daily-backups/AholaLiveBackupToTest.bak"
DATABASE_NAME="Ahola_AO_Live"
PROJECT_ID="stable-databases-01"
# Define variables for Cloud Storage Transfer
SOURCE_BUCKET_PATH="gs://ao_db_live/daily-backups/AholaLiveBackupToTest.bak"
DESTINATION_BUCKET_PATH="gs://scheduled-db-export/daily-backups/" # Note: Trailing slash for directory
# Define log file path and current timestamp
LOG_DIR="${HOME}/logs/db_exports" # Changed log directory to user's home
LOG_FILE="${LOG_DIR}/mssql_stable_export_$(date +'%Y%m%d_%H%M%S').log"
TIMESTAMP=$(date +'%Y-%m-%d %H:%M:%S')
# Ensure the log directory exists
mkdir -p "${LOG_DIR}"
# Redirect all subsequent output (stdout and stderr) to the log file
exec > >(tee -a "${LOG_FILE}") 2>&1
echo "--- Starting database export and transfer process at ${TIMESTAMP} ---"
echo "Project: ${PROJECT_ID}"
echo "-----------------------------------------------------------------"
# --- STEP 1: GCLOUD SQL EXPORT ---
echo "--- Step 1: Initiating Cloud SQL database export ---"
echo "Instance: ${INSTANCE_NAME}"
echo "Database: ${DATABASE_NAME}"
echo "Export Destination: ${BUCKET_PATH}"
echo "----------------------------------------------------"
gcloud sql export bak "${INSTANCE_NAME}" "${BUCKET_PATH}" \
--database="${DATABASE_NAME}" \
--project="${PROJECT_ID}"
# Check the exit status of the SQL export command
if [ $? -eq 0 ]; then
echo "--- Cloud SQL database export completed successfully. ---"
else
echo "--- ERROR: Cloud SQL database export failed. Aborting transfer. ---"
echo "Please check the log file for details: ${LOG_FILE}"
exit 1 # Exit with an error status if SQL export failed
fi
echo "----------------------------------------------------"
# --- STEP 2: GCLOUD STORAGE TRANSFER ---
echo "--- Step 2: Initiating Cloud Storage bucket transfer ---"
echo "Source: ${SOURCE_BUCKET_PATH}"
echo "Destination: ${DESTINATION_BUCKET_PATH}"
echo "--------------------------------------------------------"
gcloud storage cp "${SOURCE_BUCKET_PATH}" "${DESTINATION_BUCKET_PATH}"
# Check the exit status of the storage transfer command
if [ $? -eq 0 ]; then
echo "--- Cloud Storage transfer completed successfully. ---"
else
echo "--- ERROR: Cloud Storage transfer failed. ---"
echo "Please check the log file for details: ${LOG_FILE}"
exit 1 # Exit with an error status if storage transfer failed
fi
echo "--------------------------------------------------------"
echo "--- Script finished at ${TIMESTAMP} ---"
echo "All operations completed successfully."
Windows Task Scheduler jobs: * Stop AttracsService
\\bckendsrvtst-0\EazyTMS_nightly\Scripts\Manage-AttracsService.ps1 stop
\\bckendsrvtst-0\EazyTMS_nightly\Scripts\control_processes_eazytms_nightly.ps1 -action stop
NIGHTLY:
Bash script to delete old database and import new with cron job: * first delete old (Ahola_AO_EazyTMS_nightly) DB * Import latest Backup file as a new DB
#!/bin/bash
# Define variables
INSTANCE_NAME="mssql-nightly"
# This is the path to the .bak file you want to import FROM.
# Ensure this file exists in your Cloud Storage bucket.
IMPORT_BAK_PATH="gs://scheduled-db-export/daily-backups/AholaLiveBackupToTest.bak"
# This is the name of the database that will be deleted from the instance.
OLD_DATABASE_TO_DELETE="Ahola_AO_EazyTMS_nightly"
# This is the name of the database that will be created/restored to from the BAK file.
# Often, this is the same name as the deleted one to replace it.
NEW_DATABASE_TO_IMPORT="Ahola_AO_EazyTMS_nightly"
PROJECT_ID="nightly-databases-01"
# Define log file path and current timestamp
LOG_DIR="${HOME}/logs/db_operations" # Changed log directory
LOG_FILE="${LOG_DIR}/mssql_nightly_replace_db_$(date +'%Y%m%d_%H%M%S').log"
TIMESTAMP=$(date +'%Y-%m-%d %H:%M:%S')
# Ensure the log directory exists
mkdir -p "${LOG_DIR}"
# Redirect all subsequent output (stdout and stderr) to the log file
exec > >(tee -a "${LOG_FILE}") 2>&1
echo "--- Starting database replacement process at ${TIMESTAMP} ---"
echo "Project: ${PROJECT_ID}"
echo "-----------------------------------------------------------------"
# --- STEP 1: DELETE EXISTING NIGHTLY DATABASE ---
echo "--- Step 1: Deleting existing database: ${OLD_DATABASE_TO_DELETE} ---"
echo "Instance: ${INSTANCE_NAME}"
echo "----------------------------------------------------------"
# Before deleting, check if the database actually exists.
# This prevents errors if the script is re-run and the DB is already gone.
if gcloud sql databases list --instance="${INSTANCE_NAME}" --project="${PROJECT_ID}" --format="value(name)" | grep -q "^${OLD_DATABASE_TO_DELETE}$"; then
echo "Database '${OLD_DATABASE_TO_DELETE}' found. Proceeding with deletion."
gcloud sql databases delete "${OLD_DATABASE_TO_DELETE}" \
--instance="${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--quiet
if [ $? -eq 0 ]; then
echo "--- Database ${OLD_DATABASE_TO_DELETE} deleted successfully. ---"
else
echo "--- ERROR: Failed to delete database ${OLD_DATABASE_TO_DELETE}. Aborting. ---"
echo "Please check the log file for details: ${LOG_FILE}"
exit 1
fi
else
echo "Database '${OLD_DATABASE_TO_DELETE}' does not exist on instance '${INSTANCE_NAME}'. Skipping deletion."
fi
echo "----------------------------------------------------------"
# --- STEP 2: IMPORT NEW DATABASE ---
echo "--- Step 2: Initiating Cloud SQL database import ---"
echo "Instance: ${INSTANCE_NAME}"
echo "Import Source: ${IMPORT_BAK_PATH}"
echo "Target Database (within BAK): ${NEW_DATABASE_TO_IMPORT}"
echo "----------------------------------------------------"
# Start the import operation asynchronously and capture the operation ID
IMPORT_OPERATION_ID=$(gcloud sql import bak "${INSTANCE_NAME}" "${IMPORT_BAK_PATH}" \
--database="${NEW_DATABASE_TO_IMPORT}" \
--project="${PROJECT_ID}" \
--quiet \
--async --format="value(name)")
if [ -z "${IMPORT_OPERATION_ID}" ]; then
echo "--- ERROR: Failed to initiate Cloud SQL database import or retrieve operation ID. Aborting. ---"
echo "Please check the log file for details: ${LOG_FILE}"
exit 1
fi
echo "--- Import initiated with operation ID: ${IMPORT_OPERATION_ID} ---"
echo "--- Waiting for import operation to complete... ---"
gcloud sql operations wait "${IMPORT_OPERATION_ID}" --project="${PROJECT_ID}" --timeout=unlimited
# Check the status of the import operation
IMPORT_OPERATION_STATUS=$(gcloud sql operations describe "${IMPORT_OPERATION_ID}" --project="${PROJECT_ID}" --format="value(status)")
if [ "${IMPORT_OPERATION_STATUS}" == "DONE" ]; then
echo "--- Cloud SQL database import completed successfully. ---"
else
echo "--- ERROR: Cloud SQL database import failed with status: ${IMPORT_OPERATION_STATUS}. ---"
echo "Please check the log file and Google Cloud Console for operation ${IMPORT_OPERATION_ID} details."
exit 1 # Exit with an error status if SQL import failed
fi
echo "----------------------------------------------------"
echo "--- Script finished at ${TIMESTAMP} ---"
echo "Database replacement process completed."
Windows Task Scheduler jobs: * Start AttracsService
\\bckendsrvtst-0\EazyTMS_nightly\Scripts\Manage-AttracsService.ps1 start
\\bckendsrvtst-0\EazyTMS_nightly\Scripts\control_processes_eazytms_nightly.ps1 -action start
SQL Agent
Note that SQL Agent work in UTC 0 time!
Add database user to new EazyTMS_AO_Nightly database.
-- AddUserToDatabase.sql
-- This part will be skipped if 'YourLoginName' already exists.
-- If it's a Windows Login, ensure the correct format (e.g., 'YourDomain\YourWindowsUser').
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = 'ao_eazytms_nightly')
BEGIN
-- For SQL Server Authentication (if you ever need to create a new one):
-- CREATE LOGIN [YourLoginName] WITH PASSWORD = 'YourStrongPassword!', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
-- For Windows Authentication (if you ever need to create a new one):
-- CREATE LOGIN [YourDomain\YourWindowsUser] FROM WINDOWS WITH DEFAULT_DATABASE = [master];
PRINT 'Login [ao_eazytms_nightly] already exists or will be created if not found.';
END
GO
-- Use the target database where you want to add the user
USE [Ahola_AO_EazyTMS_nightly]; -- **IMPORTANT: Replace YourDatabaseName with your actual database name**
GO
-- Create the User in the database and map it to the existing Login
-- Using the provided username: ao_eazytms_nightly
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = 'ao_eazytms_nightly')
BEGIN
CREATE USER [ao_eazytms_nightly] FOR LOGIN [ao_eazytms_nightly]; -- **IMPORTANT: Replace YourLoginName with the actual existing login name**
PRINT 'User [ao_eazytms_nightly] created in database [Ahola_AO_EazyTMS_nightly] for login [ao_eazytms_nightly].';
END
ELSE
BEGIN
PRINT 'User [ao_eazytms_nightly] already exists in database [Ahola_AO_EazyTMS_nightly].';
END
GO
-- Grant permissions to the user: db_datareader and db_datawriter
ALTER ROLE [db_datareader] ADD MEMBER [ao_eazytms_nightly];
ALTER ROLE [db_datawriter] ADD MEMBER [ao_eazytms_nightly];
PRINT 'User [ao_eazytms_nightly] granted db_datareader and db_datawriter permissions in database [Ahola_AO_EazyTMS_nightly].';
****
Next Step:
SQL Agent will run job: evolution-to-eazytms-nightly
* evolution_main.sql from latest version (need to update manually, when there is changes to script)
Next: * SQL Agent will execute compatibility level upgrade with these commands:
-- Apply on the eazytms-nightly database (substitute the real DB name):
ALTER DATABASE [<eazytms-nightly DB>] SET COMPATIBILITY_LEVEL = 150;
-- Safety valve — get IQP features but keep the old estimator until CE is verified:
ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = ON;
GCLOUD COMMANDS:
EXPORT DATABASE
gcloud sql export bak mssql-stable gs://ao_db_live/daily-backups/AholaLiveBackupToTest.bak \
--database=Ahola_AO_Live \
--project=stable-databases-01
BUCKET TRANSFER
gcloud storage cp gs://ao_db_live/daily-backups/BackupToTest.bak gs://scheduled-db-export/daily-backups/
DELETE DATABASE
gcloud sql databases delete Ahola_AO_EazyTMS_nightly --instance=mssql-nightly --project=nightly-databases-01 --quiet
IMPORT DATABASE
gcloud sql import bak mssql-nightly gs://scheduled-db-export/daily-backups/BackupToTest.bak \
--database=Ahola_AO_EazyTMS_nightly \
--project=nightly-databases-01 \
--quiet