Oct 302008
 

With all the databases I run, I need a daily sanity check. I download a copy of each database and do a test load on my system at home. Today I timed that process:

$ time ~/bin/backup-supernews.sh

real 77m57.323s
user 12m52.610s
sys 0m56.136s

There are 11 such databases. The dump files range in size from 5K (globals.sql) to 2.8G (freshports.sql).

The script that does this is pretty simple, but has evolved over the years.

#!/bin/sh
#
# This file does a backup of each database on the nyi server.
# It relies upon a file on nyi to do the actual backup,
# then uses rsync to copy the files from nyi to here.
#

BACKUPDIR=${HOME}/backups/supernews/database-backup

IDENTITY_FILE=${HOME}/.ssh/id_dsa_no_passphrase
SERVER_TO_BACKUP=supernews.example.org

cd ${BACKUPDIR}/postgresql

YYYYMMDD=`eval date "+%Y.%m.%d"`
echo will tar things up into ${YYYYMMDD} format

FILES=`echo *.sql`
echo will be processing ${FILES}
# maybe there are no such files
if [ "x${FILES}" != 'x\*.sql' ]
then
   for i in $FILES
   do
#      mv ${i} archive
      tar -cvzf archive/`basename ${i} .sql`.${YYYYMMDD}.sql.tgz ${i}
   done
else
   echo no files found for processing
fi


cd ${BACKUPDIR}/mysql
FILES=`echo *.sql`

# maybe there are no such files
if [ "x${FILES}" != 'x\*.sql' ]
then
   for i in $FILES
   do
#      mv ${i} archive
      tar -cvzf archive/`basename ${i} .sql`.${YYYYMMDD}.sql.tgz ${i}
   done
fi

cd ${BACKUPDIR}


#
# dump the databases on the remote server
#
logger "dumping the databases on ${SERVER_TO_BACKUP} from `hostname`"
ssh -i ${IDENTITY_FILE} dan@${SERVER_TO_BACKUP} ~dan/bin/backup.sh

#
# use rsync to get local copies
#
/usr/local/bin/rsync -e "ssh -i ${IDENTITY_FILE}" --recursive -avz --stats --progress \
--exclude 'archive' dan@${SERVER_TO_BACKUP}:~/database-backup/ ${BACKUPDIR}

#
# now do a test of the backups to ensure they actually restore

~/bin/test-backups.sh ${BACKUPDIR}

That is the job that archives the old files and downloads the new ones.

The following script loads up that database and is referred to in the last line of the above script:

#!/bin/sh
#
# test the files we've recovered
#

BACKUPDIR=$1
GLOBALS="globals"

PSQL=/usr/local/bin/psql
CREATEDB=/usr/local/bin/createdb
DROPDB=/usr/local/bin/dropdb

cd ${BACKUPDIR}/postgresql

echo dropping test databases
FILES=`echo *.sql`
for i in $FILES
do
        DBNAME=`basename $i .sql`
        if [ ${DBNAME} != ${GLOBALS} ]
        then
                ${DROPDB} ${DBNAME}_test
        fi
done

echo loading GLOBALS
if [ -r "${GLOBALS}.sql" ]
then
        ${PSQL} postgres < ${GLOBALS}.sql
fi

echo creating the databases
for i in $FILES
do
        DBNAME=`basename $i .sql`
        if [ ${DBNAME} != ${GLOBALS} ]
        then
                ${CREATEDB} ${DBNAME}_test -E SQL_ASCII
                ${PSQL}     --quiet ${DBNAME}_test < ${DBNAME}.sql
        fi
done

The final part of the puzzle is the script run on the production server which dumps all the databases required:

#!/bin/sh

PGDUMP=/usr/local/bin/pg_dump
MYSQLDUMP=/usr/local/bin/mysqldump
BACKUPROOT=${HOME}/database-backup

cd ${BACKUPROOT}/postgresql

DATABASES="bsdcert fpphorum freshports.org fsphorum openx nagios \
pentabarf pentabarf_bsdcan pentabarf_pgcon postgres"
for database in ${DATABASES}
do
        echo dumping $database
        if [ -f ${database}.sql ]
        then
                mv ${database}.sql archive
        fi

        ${PGDUMP} ${database} > ${database}.sql

        if [ $? -ne 0 ]
        then
                echo backup of ${database} failed
        fi

done

#
# Now dump the globals
#

if [ -f globals.sql ]
then
        mv globals.sql archive
fi

/usr/local/bin/pg_dumpall -g  > globals.sql

if [ $? -ne 0 ]
then
        echo backup of globals failed
fi



DATABASES="cacti mysql"
cd ${BACKUPROOT}/mysql

for database in ${DATABASES}
do
    echo dumping $database
    if [ -f ${database}.sql ]
    then
        mv ${database}.sql archive
    fi
    ${MYSQLDUMP} -u root ${database} > ${database}.sql
    if [ $? -ne 0 ]
    then
       echo backup of ${database} failed
    fi
done

Any questions? :) Continue reading »

Oct 232008
 

I’ve been running ads on my websites since early 1999, so nearly 10 years. It was roughly May 2002 that I started using phpPgAds. This was the PostgreSQL version of phpAdsNew. This project became OpenAds and is now known as OpenX.

Recently I decided to upgrade to the latest version of OpenX. There was a upgrade path from Openads-PgSQL-2.0.11-pr1, the latest releast of OpenAds to use PostgreSQL, to OpenX 2.6.

The first problem I encountered was related to timezones. The fix was easy, but difficult to obtain. It look like this, and affected lib/OA/Upgrade/Login.php in two places.

- $oDbh->exec("SET TIMEZONE TO 'UTC'");
+ $oDbh->exec("SET TIMEZONE TO 'GMT'");

The problem: UTC is not a valid PostgreSQL timezone. GMT is.

I posted full details in the OpenX forum.

Later, I started seeing this error in my logs:

ERROR:  invalid input syntax for type timestamp: "%8-%m-%5 %H:00:00"
STATEMENT:
                       INSERT INTO
                           tmp_ad_impression
                           (
                               date_time,
                               operation_interval,
                               operation_interval_id,
                               interval_start,
                               interval_end,
                               ad_id,
                               creative_id,
                               zone_id,
                               impressions
                          )
                      SELECT
                         DATE_FORMAT(drad.date_time, '%Y-%m-%d %H:00:00')::timestamp AS day_and_hour,
                          60 AS operation_interval,
                          98 AS operation_interval_id,
                          '2008-10-23 02:00:00'::timestamp AS interval_start,
                          '2008-10-23 02:59:59'::timestamp AS interval_end,
                          drad.ad_id AS ad_id,
                          drad.creative_id AS creative_id,
                          drad.zone_id AS zone_id,
                          COUNT(*) AS impressions
                      FROM
                          "phpads_data_raw_ad_impression" AS drad
                      WHERE
                         drad.date_time >= '2008-10-23 02:00:00'
                         AND
                         drad.date_time < = '2008-10-23 02:59:59'
                      GROUP BY
                          day_and_hour, drad.ad_id, drad.creative_id, drad.zone_id

I got onto the OpenX IRC channel and started asking questions. Eventually, Matteo Beccati came to my rescue. Matteo and I go way back. I was one of the first users of the PostgreSQL version of this product. I chose it *because* it worked with PostgreSQL. I was not interested in the MySQL version.

We found the cause of the problem quickly enough:

\df date_format
  Schema |    Name     | Result data type |        Argument data types        
 --------+-------------+------------------+-----------------------------------
  public | date_format | text             | timestamp with time zone, text
  public | date_format | text             | timestamp without time zone, text

The solution:

drop function date_format(timestamp, text)

My database had been upgraded and upgraded over the years. So this functions lingered. He suggested I look at https://svn.openx.org/openx/trunk/etc/core.pgsql.php and compare it to what I had. Most of their testing had been upgrades a fresh 2.0.11 install.

Searching through the database, function by function, I found the following issues:

openx=# \df HOUR
                       List of functions
 Schema | Name | Result data type |     Argument data types     
--------+------+------------------+-----------------------------
 public | hour | integer          | timestamp with time zone
 public | hour | smallint         | timestamp without time zone
(2 rows)

openx=# drop function hour(timestamp without time zone);
DROP FUNCTION


openx=# \df TO_DAYS
                         List of functions
 Schema |  Name   | Result data type |     Argument data types     
--------+---------+------------------+-----------------------------
 public | to_days | bigint           | timestamp without time zone
 public | to_days | integer          | timestamp with time zone
(2 rows)

openx=# drop function to_days(timestamp without time zone);
DROP FUNCTION



openx=# \df UNIX_TIMESTAMP
                            List of functions
 Schema |      Name      | Result data type |     Argument data types     
--------+----------------+------------------+-----------------------------
 public | unix_timestamp | bigint           | timestamp without time zone
 public | unix_timestamp | integer          | timestamp with time zone
(2 rows)

openx=# drop function unix_timestamp(timestamp without time zone);
DROP FUNCTION

Hopefully that will fix any hidden problems.

I’m still not running this in production yet. I’m waiting for OpenX 2.6.2 to hit the FreeBSD ports tree.

Oct 212008
 

I have several items I would like to complete in the short term. Writing them down may help me commit to doing them.

To Do

  • Upgrade the NYI server to the latest version of PostgreSQL
  • Prepare the BSDCan and PGCon websites for 2009 (partly done)
  • Get MacPorts running on my MacBook
  • Get spamd running on the new server
  • Add a check to Nagios to check that system email arrives each day (e.g. ‘security run output’, ‘daily run output’)
  • Fix up my DVR and ActionTech router so it works with just one network connection
  • Finish off the Bacula migration story
  • Do some more work on OpenVPN CRL and certificates with passphrases (added 2008-12-01)
  • add items to this list that I have forgotten about

Started

Done

  • Adjust Nagios monitoring of my Nagios box to include a specific check for one FS
  • Move my FreeBSD gateway to be between the internet and my Verizon router – 22 Nov 2008
  • Adjust my Nagios installation so I can easily access it when not at home – 29 Nov 2008 : can access my home websites via the new VPN
  • Create a VPN between home and the outside servers so I don’t have to deal with static IP problems – 29 Nov 2008
Oct 162008
 

Tonight I encountered this problem when starting stunnel:

2008.10.17 02:46:02 LOG7[43534:134656000]: Private key loaded
2008.10.17 02:46:02 LOG7[43534:134656000]: SSL context initialized for service 6000
2008.10.17 02:46:02 LOG5[43534:134656000]: stunnel 4.25 on i386-portbld-freebsd6.3 with OpenSSL 0.9.7e-p1 25 Oct 2004
2008.10.17 02:46:02 LOG5[43534:134656000]: Threading:PTHREAD SSL:ENGINE Sockets:POLL,IPv6 Auth:LIBWRAP
2008.10.17 02:46:02 LOG6[43534:134656000]: file ulimit = 11095 (can be changed with 'ulimit -n')
2008.10.17 02:46:02 LOG6[43534:134656000]: poll() used - no FD_SETSIZE limit for file descriptors
2008.10.17 02:46:02 LOG5[43534:134656000]: 5417 clients allowed
2008.10.17 02:46:02 LOG7[43534:134656000]: FD 5 in non-blocking mode
2008.10.17 02:46:02 LOG7[43534:134656000]: FD 6 in non-blocking mode
2008.10.17 02:46:02 LOG3[43534:134656000]: local socket: Protocol not supported (43)

And stunnel would not run. I found one hint in the FreeBSD mailing list archives.

Oddly enough, this machine is running FreeBSD 6.3-STABLE and the post above involves 6.2. Perhaps something in common.

I added this entry to my kernel.

options 	INET6	# added to fix stunnel problem: "local socket: Protocol not supported"

It is compiling now.

Oct 062008
 

There are several new interesting features in the upcoming Bacula release. Including, but not limited to:

  • “Accurate” backups regardless of mtime, mv, rm, etc
  • Copy Jobs
  • Virutal Backup / Synthetic Backup / Consolidation
  • Duplicate Job Control
  • TLS Authentication
  • Ignore Dir
  • Honor No Dump Flag
  • Plugins
  • libdbi framework

These features are being added to the development 2.5.x versions to be released as Bacula version 3.0.0. This release is expected to occur near the end of 2008.

Disclosure: I am a Bacula Committer.