I recently worked with a client that had a custom-written application that used Actian Zen (previously known as Pervasive SQL) as its back-end database. I wanted to make a backup of the data – and not just a single, manual backup, but a nightly, automated backup. I developed a script that works perfectly for what I need – below is the script, along with an explanation of it. It’s made for Windows Server 2012, although it should work the same for Windows Server 2008 as well. Hope it helps!
This script does the following:
Let’s cut right to the chase with the script itself. You can also move on to the setup section below to find out how to use it, as well as the explanation section, which I really suggest before implementing this solution in production.
Download the Script – Rename .txt to .bat
@ECHO off
::Set the variables
set dataLocation=C:\PervasiveSQLDBData\
set backupLocation=D:\Database_Backup\
set daysToKeepBackups=10
set installLocation=C:\Program Files (x86)\Pervasive Software\PSQL\PBA\BIN\
:::Set the date/time variables
::Time, from http://stackoverflow.com/questions/1192476/format-date-and-time-in-a-windows-batch-script
set hour=%time:~0,2%
if "%hour:~0,1%" == " " set hour=0%hour:~1,1%
set min=%time:~3,2%
if "%min:~0,1%" == " " set min=0%min:~1,1%
set secs=%time:~6,2%
if "%secs:~0,1%" == " " set secs=0%secs:~1,1%
::Date, from http://stackoverflow.com/a/3475477/774359
for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date
:set_date
if "%1:~0,1%" gtr "9" shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof
:end_set_date
::Create the datetime string and create a folder with that name
set datetime=%yy%_%mm%_%dd% %hour%_%min%_%secs%
mkdir "%backupLocation%\%datetime%"
::Put the database in Continuous Operations Mode
cd %installLocation%
pvbackup -on
echo.
echo Copying files:
echo ---------------
::Copy all .mkd files
xcopy "%dataLocation%*.mkd" "%backupLocation%%datetime%" /y
::Copy all .ddf files
xcopy "%dataLocation%*.ddf" "%backupLocation%%datetime%" /y
::Take the database off of Continuous Operations Mode
pvbackup -off
echo.
echo Deleting files:
echo ---------------
::Delete backup folders that are older than 10 days
ForFiles /P %backupLocation% /D -%daysToKeepBackups% /C "CMD /C if @ISDIR==TRUE rmdir /Q /S @FILE"
::Alternative: delete folders that aren't named "importantFolder" - useful if you have a ".sync" or ".dropbox" folder used by another program to index the backup folder
::ForFiles /P %backupLocation% /D -%daysToKeepBackups% /C "CMD /C if @ISDIR==TRUE rmdir /Q /S @FILE|find /v /i ""importantFolder"""
There are a few things you’ll need in order to set up this script. The first is Actian Zen SQL installed and running – I assume you have that already. The second thing you’ll need to do is install the Actian ZenBackup Agent. Which one you need will depend on your license – my server currently has database version 3.2, 32 bit installed, so I therefore went to the “Backup Agent v3.2 Downloads” section and found the “Backup Agent v3.2 – Windows” row in the table, and then clicked on the “32-bit” link to download it. After installing it, I was able to use the pvbackup
command. If is installed correctly, running pvbackup
from the command line should show you some copyright information as well as usage information – if it’s installed incorrectly, it will tell you that “pvbackup is not a recognized command”.
Installed correctly
Installed incorrectly
The next thing you’re going to want to do is modify the batch file. The explanation for why I wrote it this way is below, and is necessary reading to make sure you don’t mess up your existing installation – this is more of a quick guide for setting up the script.
C:\PervasiveSQLDBData\
. Since this most likely is not where your data is, you will have to change the variable on line 3. Remember – the data location is the folder in which all of your .mkd and .ddf files reside.D:\Database_Backup
. If you intend to store your backup files elsewhere, you will have to change the variable on line 4.C:\Program Files (x86)\Pervasive Software\PSQL\PBA\BIN\
as its installation location. If you installed the Actian Zen Backup Agent in a different place, you’ll need to change this line or the pvbackup -on
and other pvbackup commands will fail. If this is not the correct location for you, you will need to change line 6. Note that if you installed the 64-bit version of Actian Zen Backup Agent, the “(x86)” will need to be removed from this path.Now for the fun part – an explanation of what each section of the script does.
@ECHO off
::Set the backup location
set dataLocation=C:\PervasiveSQLDBData\
set backupLocation=D:\Database_Backup\
set daysToKeepBackups=10
set installLocation=C:\Program Files (x86)\Pervasive Software\PSQL\PBA\BIN\
This section is relatively simple – first, we turn @ECHO off
, which basically makes the command prompt not show us all of the commands as they run, but instead only show us the output of the commands. Removing this line can help a lot when it comes to debugging the script, should it fail for some reason. The next lines beginning with set
are setting variables, which will be used throughout the script, but are set in one convenient place at the top.
:::Set the date/time variables
::Time, from http://stackoverflow.com/questions/1192476/format-date-and-time-in-a-windows-batch-script
set hour=%time:~0,2%
if "%hour:~0,1%" == " " set hour=0%hour:~1,1%
set min=%time:~3,2%
if "%min:~0,1%" == " " set min=0%min:~1,1%
set secs=%time:~6,2%
if "%secs:~0,1%" == " " set secs=0%secs:~1,1%
::Date, from http://stackoverflow.com/a/3475477/774359
for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date
:set_date
if "%1:~0,1%" gtr "9" shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof
:end_set_date
::Create the datetime string and create a folder with that name
set datetime=%yy%_%mm%_%dd% %hour%_%min%_%secs%
mkdir "%backupLocation%\%datetime%"
This is a pretty confusing code block, which uses a number of system calls as well as string manipulation to get to a fairly simple result – the ability to use variables for year, month, day, hours, seconds, and minutes – in order to set up a datetime
variable, which is then used to create a folder using that datetime variable in the backup location. This allows for a consistent naming scheme for folders, inside which we will soon put the backed-up files. I got a lot of help crafting these code blocks from two StackOverflow answers, whose links I put in comments in the code for future reference as well as because I like to cite my sources. Note that the time here is based on the server’s local time.
::Put the database in Continuous Operations mode
cd %installLocation%
pvbackup -on
This is the crux of the database backup part of the script – the pvbackup -on
command puts the database in “Continuous Operations Mode”. You can find more information about it here – basically, when the database is in use, it writes the data to the .mkd files. However, we want to copy the .mkd files as a backup – this poses a problem. How can we copy a file that is actively being changed? Actian Zenmakes this easy: just put the database into Continuous Operations Mode. What that does is lock the .mkd files so that they will not be changed, allowing us to copy them. This does not, however, stop the database from being useable – instead, all changes are written to delta files, which use the .^^^ file extension. This does have some performance impact, though, so I wouldn’t suggest leaving Continuous Operations Mode on for long – at the end of the script, we turn it off. Note that if for some reason the script fails midway through, you may have to run pvbackup -off
manually to turn it off. If it’s already off, running that command will not hurt anything. You can also run pvbackup -status
to find out whether it’s currently on or off.
::Copy all .mkd files
xcopy "%dataLocation%*.mkd" "%backupLocation%%datetime%" /y
::Copy all .ddf files
xcopy "%dataLocation%*.ddf" "%backupLocation%%datetime%" /y
These lines simply copy the .mkd and .ddf files (database and data definition files) from the data location and put them into the backup location, specifically into the folder named by the date and time calculated above.
::Take the database off of Continuous Operations Mode
pvbackup -off
Very important – we need to turn Continuous Operations Mode off. This will roll all of the delta files back into the main database in the background – again, because your database is doing more things, you’ll have a slight performance impact, but unless you’re in a high-performance environment you probably won’t notice at all. Once all of the delta files are rolled back into the database, everything will be back to normal.
::Delete backup folders that are older than 10 days
ForFiles /P %backupLocation% /D -%daysToKeepBackups% /C "CMD /C if @ISDIR==TRUE rmdir /Q /S @FILE"
One issue with using backup scripts that don’t overwrite old backups is that they can grow out of control pretty quickly. If you run this backup every hour, and your database is 1GB, then your backup will be growing by 24GB/day. You can do some complex compression if your data is relatively similar day-to-day, but it doesn’t look like Actian Zen SQL offers any kind of differential backup natively (which would provide you with the only the changes since the last backup). Because of that, I chose to simply create a new backup every time the script ran – which brings you to the problem of having way too many backups after some time. To mitigate this issue, every time the script runs, it simply deletes backups older than a certain number of days (based on the Date Modified file property). Personally, I have this set to 10 – based on my use case, I will never need a backup older than 10 days. Depending on your use case, even having more then a single copy may be overkill – but as long as you have the space to store the backups, you might as well. Note that the if @ISDIR==TRUE
part of the code checks to make sure that what you’re deleting is a directory – it will not delete other files in the backups directory, so that might be a good spot to put the backup script.
::Alternative: delete folders that aren't named "importantFolder" - useful if you have a ".sync" or ".dropbox" folder used by another program to index the backup folder
::ForFiles /P %backupLocation% /D -%daysToKeepBackups% /C "CMD /C if @ISDIR==TRUE rmdir /Q /S @FILE|find /v /i ""importantFolder"""
Ideally, you wouldn’t have anything in your backup folder besides the folders that this script creates, and possibly the script file itself. However, I ran into a problem using BitTorrent Sync to synchronize these backups to an offsite location (a process which I describe in this post, using Resilio Sync as my synchronization software). The problem is that BitTorrent Sync creates a folder named .sync, which is used to store the index it builds of the directory. Deleting that .sync folder causes the program to display an error, and syncing of that folder stops completely. The solution? Just delete folders older than a certain age, but ignore the .sync folder completely, leaving it there permanently. To do that specifically, replace importantFolder
with .sync
. Works with any other single folder as well. Credit to user “Magoo” on StackOverflow for helping me with the string comparison, which is much more difficult than I would have expected.
That’s it! I’ve had this script running for a few weeks now with no issues. Have any questions or comments? Let me know below!
Thanks for this information
However do you mean that with PervasiveSQL you can only restore from the Datafiles backup ?
If you do a Datafile backups nightly and your database crashed during the day, you would only be able to restore from the Datafile backup thus losing all the data that occured from the last Datafile backup till the DB crash ?
Anyway to have regular kind of “transaction log” backups like in SQL Server ?
Hey Steve, I requested information form the PervasiveSQL people and got this as an answer – I’m not sure if they’ve come up with something like a transaction log since then, but unfortunately my solution was just a nightly backup.
Snapshot backups, using either Backup Agent (which works with both Workgroup Engines and Server Engines) or VSS Snapshots (which works only with the Server Engines) should always be fully restored to make sense. (You can find out more about WHY this is the case on the Goldstar Software web site.)
If your database is small enough and you need an intra-day backup, then just take one every hour or so to have many more restore options.
For continuous replication, Carbonite Availability is a great way to protect your data with a very low RPO.