Tuesday, October 19, 2010

mod_jk instalation on ubuntu 9.4

Assuming Tomcat and Apache are installed and running

1. Install the mod_jk
sudo su -
apt-get install libapache2-mod-jk

2. Enable md_jk loading
cd /etc/apache2/mods-enabled
ln -s jk.load ../mods-available/jk.load

3. Create the mod_jk conf file
cd /etc/apache2/mods-available
vi jk.conf
# Where to find workers.properties
# Update this path to match your conf directory location
JkWorkersFile /etc/libapache2-mod-jk/workers.properties

# Where to put jk logs
# Update this path to match your logs directory location
JkLogFile /var/log/apache2/mod_jk.log

# Set the jk log level [debug/error/info]
JkLogLevel info

# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"

# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"

# Shm log file
JkShmFile /var/log/apache2/jk-runtime-status

4.Enable mod_jk configurations
cd ../mods-enabled
ln -s jk.conf ../mods-available/jk.conf

5. Create a worker properties file
vi /etc/libapache2-mod-jk/workers.properties
workers.tomcat_home=/var/lib/tomcat6
workers.java_home=/usr/lib/jvm/java-6-sun
ps=/
worker.ajp13_worker.port=8009
worker.ajp13_worker.host=localhost
worker.ajp13_worker.type=ajp13
worker.ajp13_worker.lbfactor=1

6.Configure url forwarding from Apache to Tomcat
cd /etc/apache2/sites-enabled
vi 000-default ##Which is a symbolic link to ../sites-available/default

...
JkMount /examples/ ajp13_worker
JkMount /examples/* ajp13_worker
JkMount /docs/ ajp13_worker
JkMount /docs/* ajp13_worker
...

##This will forward any request to /examples/ and /docs/ to Tomcat

7. Configure AJP in Tomcat Server.
vi /etc/tomcat6/server.xml
##Uncomment the following line


8. Restart Apache and Tomcat
/etc/init.d/tomcat6 restart
/etc/init.d/apache2 restart

9. Test it
Open a browser
http://localhost:8080/examples
Then remove the port
http://localhost:/examples

You must see the same content, so Apache is forwawrding the request to Tomcat to show /examples content

Friday, October 8, 2010

Postgres Stream replication set up

Master
-IP: 192.168.1.1
-TCP Port: 5432
-Data directory: /var/lib/pgsql/9.0/data
-WAL directory: /var/lib/pgsql/9.0/data/wals

Slave
-IP: 192.168.1.2
-TCP Port: 5432
-Data directory: /var/lib/pgsql/9.0/data
-WAL directory: /var/lib/pgsql/9.0/data/wals

Master configuration: ($ /var/lib/pgsql/9.0/data/postgresql.conf)
-listen_addresses = '*'
-port = 5432
-archive_mode = on
-archive_command = 'cp "%p" /var/lib/pgsql/9.0/data/wals/"%f" < /dev/null'
-max_wal_senders = 1
-wal_level = hot_standby
-hot_standby = on

Authentication Configuration: ($ /var/lib/pgsql/9.0/data/pg_hba.conf)
-host replication all 10.16.35.213/23 trust

Start Up the Master
/usr/pgsql-9.0/bin/postgres -D /var/lib/pgsql/9.0/data &

Make a Base Backup for stand by from the master (Snapshot)
$ psql -c "SELECT pg_start_backup('base_backup')"
$ cd 9.0/data
$ scp -rp x postgres@10.16.35.213:/var/lib/pgsql/9.0/data
$ psql -c "SELECT pg_stop_backup()"

Slave Configuration ($ /var/lib/pgsql/9.0/data/postgresql.conf)

-listen_addresses = '*'
-port = 5432
-archive_mode = on
-archive_command = 'cp "%p" /var/lib/pgsql/9.0/data/wals/"%f" < /dev/null'
-max_wal_senders = 1
-wal_level = hot_standby
-hot_standby = on

$ rm -fr /var/lib/pgsql/9.0/data/postmaster.pid
$ rm -fr /var/lib/pgsql/9.0/data/pg_xlog/000*
$ rm -fr /var/lib/pgsql/9.0/data/pg_xlog/archive_status/*
$ rm -fr /var/lib/pgsql/9.0/data/wals/*

Recovery configuration on the slave: (/var/lib/pgsql/9.0/data/recovery.conf)
standby_mode = 'on'
primary_conninfo = 'host=192.168.1.1 port=5432 user=postgres password=postgres'
restore_command = 'cp -i /var/lib/pgsql/9.0/data/wals/%f %p < /dev/null'

Start up the slave
/usr/pgsql-9.0/bin/postgres -D /var/lib/pgsql/9.0/data &

Check the process
$ ps -fea | grep postgres
root 12121 11960 0 19:43 pts/1 00:00:00 su - postgres
postgres 12122 12121 0 19:43 pts/1 00:00:00 -bash
postgres 12823 12122 0 20:23 pts/1 00:00:00 /usr/pgsql-9.0/bin/postgres -D /var/lib/pgsql/9.0/data
postgres 12824 12823 0 20:23 ? 00:00:00 postgres: logger process
postgres 12825 12823 0 20:23 ? 00:00:00 postgres: startup process recovering 00000001000000000000000C
postgres 12828 12823 0 20:23 ? 00:00:00 postgres: wal receiver process streaming 0/C024038
postgres 12829 12823 0 20:23 ? 00:00:00 postgres: writer process
postgres 12830 12823 0 20:23 ? 00:00:00 postgres: stats collector process
postgres 12901 12122 0 20:59 pts/1 00:00:00 ps -fea
postgres 12902 12122 0 20:59 pts/1 00:00:00 grep postgres

Test it

Insert some data in the master:

$ psql
postgres=# create table t (i int);
postgres=# insert into t values(1);
postgres=# insert into t values(2);
postgres=# select * from t;
i
---
1
2
(2 rows)

Check in the Slave
$ psql
postgres=# select * from t;
i
---
1
2
(2 rows)

Friday, August 27, 2010

Transpose rows into column. Example

mysql> select * from site_parts;
+---------+-----------+------+
| site | part | qty |
+---------+-----------+------+
| Sunrise | HD500 | 50 |
| Sunrise | NIC1000 | 10 |
| Miami | HD500 | 20 |
| Miami | Monitor20 | 10 |
| Miami | HD500 | 5 |
| NY | Mouse | 40 |
+---------+-----------+------+
6 rows in set (0.00 sec)

mysql> select
site,
sum(if(part='HD500',qty,0)) as HD500,
sum(if(part='NIC1000',qty,0)) as NIC1000,
sum(if(part='Monitor20',qty,0)) as Monitor20,
sum(if(part='Mouse',qty,0)) as Mouse
from
site_parts group by site;

+---------+-------+---------+-----------+-------+
| site | HD500 | NIC1000 | Monitor20 | Mouse |
+---------+-------+---------+-----------+-------+
| Miami | 25 | 0 | 10 | 0 |
| NY | 0 | 0 | 0 | 40 |
| Sunrise | 50 | 10 | 0 | 0 |
+---------+-------+---------+-----------+-------+
3 rows in set (0.00 sec)

Monday, August 2, 2010

Merge table management using MySQL event scheduler and Stored Procedures

Normally we use cronjobs to execute scheduled tasks, but some times because of security policies we don't have access to cronjobs, this is even worse in safe environments, in those cases, it is quite possible we do not have access to the file system and the only possible access is though MySQL.

So what can we do when we need to run scheduled tasks in MySQL?.

Fortunately, there is an event scheduler in MySQL; using this feature we can execute basically any MySQL scehduled job.

In this case it is required to create merge table based on pre-define schedule that can be minutes, hours, days, months or years. Yes!, I know that now would bemuch better to use partitions but in this case this is the requirement.

To reach the gaol the following stored procedures were implemented:

1.table_management_create_new_merge: Create the merge table based on the create table statement and a template
2.table_management_drop_merge: Deletes the merge table and all Underlying MyISAM tables
3.table_management_alter_merge: Changes the table and all merge MyISAM tables Underlying
4.table_management_add_new_table_to_merge: Adds a new table to merge
5.table_management_drop_table_from_merge: Take out a MyISAM table from the merge

This is a very basic merge table management system, what is more important in this case is to add and exclude the merge tables on cheduled basis, however, I decided to implement all the necessary procedures to make easier the administration of these Merge tables.

I want o emphasize two things that made this a little more complex than initially thought:


1. MySQL does not store any part of its metadata (information_schema) the relationship between merge MyISAM tables and tables that are part of this. Stores it in a file. MRG which I have no access!


2. I was not able by a cursor or variable assign the result of "SHOW CREATE TABLE" which was the second place where it could achieve the relationship between the merge table and MyISAM tables Underlying!. If someone knows please let me know.

Ok, there was no other choice but to implement a basic data structure what I will use record the relationship between the Merge table and its Underlying MyISAM tables.

Lets go back to goal: add / exclude table of a merge table on scheduled basis.

Here is the procedure:


1. Create the data structure that stores the relationship between the merge table and the underlying mysam tables:


create database table_management;

CREATE TABLE `dbschemas` (
`schema_id` int(11) NOT NULL AUTO_INCREMENT,
`schema_name` varchar(64) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`schema_id`),
UNIQUE KEY `schema_name_UNIQUE` (`schema_name`)
) ENGINE=InnoDB;


CREATE TABLE `dbtables` (
`table_id` int(11) NOT NULL AUTO_INCREMENT,
`schema_id` int(11) NOT NULL,
`table_name` varchar(64) DEFAULT NULL,
`parent_table_id` int(11) DEFAULT NULL,
PRIMARY KEY (`table_id`,`schema_id`),
UNIQUE KEY `schema_id_table_name_UNIQUE` (`schema_id`,`table_name`),
KEY `fk_table_schema1` (`schema_id`),
KEY `fk_table_table1` (`parent_table_id`),
CONSTRAINT `fk_table_schema1` FOREIGN KEY (`schema_id`) REFERENCES `dbschemas` (`schema_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_table_table1` FOREIGN KEY (`parent_table_id`) REFERENCES `dbtables` (`table_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;


2. Create the required stored procedures:


##################Create new merge table and its template MyISAM TABLE#################

drop procedure table_management_create_new_merge;
delimiter //
#The parameter create_stmt is a regular table create statement, it does not include the union neither the engine.
create procedure table_management_create_new_merge (target_schema varchar(64),create_stmt varchar(10000))
begin
declare merge_table_name varchar(64);
declare column_def varchar(10000);
declare v_merge_table_id int;
declare v_schema_id int;

select trim(substring(trim(substring(create_stmt,locate('table',create_stmt)+5)),1,locate('(',trim(substring(create_stmt,locate('table',create_stmt)+5)))-1)) into @merge_table_name;
select substring(trim(substring(create_stmt,locate('table',create_stmt)+5)),locate('(',trim(substring(create_stmt,locate('table',create_stmt)+5)))) into @column_def;

set @create_myisam_template_table_stmt=concat('create table ',target_schema,'.',@merge_table_name,'_tmpt ',@column_def,' ENGINE=MyISAM');
prepare create_myisam_template_table_stmt from @create_myisam_template_table_stmt;
execute create_myisam_template_table_stmt;

set @create_merge_table_stmt=concat('create table ',target_schema,'.',@merge_table_name,' ',@column_def,' ENGINE=MRG_MyISAM UNION=(',target_schema,'.',@merge_table_name,'_tmpt)');
prepare create_merge_table_stmt from @create_merge_table_stmt;
execute create_merge_table_stmt;

start transaction;

insert ignore into table_management.dbschemas set schema_name=target_schema;
select schema_id into v_schema_id from table_management.dbschemas where schema_name=target_schema;
insert into table_management.dbtables set schema_id=v_schema_id,table_name=@merge_table_name;
select table_id into v_merge_table_id from table_management.dbtables where schema_id=v_schema_id and table_name=@merge_table_name;
insert into table_management.dbtables set schema_id=v_schema_id,table_name=concat(@merge_table_name,'_tmpt'),parent_table_id=v_merge_table_id;

commit;
deallocate prepare create_myisam_template_table_stmt;
deallocate prepare create_merge_table_stmt;
end//
delimiter ;

#################Drop a Merge table and all its underlying MyISAM tables #########################
drop procedure table_management_drop_merge;
delimiter //
create procedure table_management_drop_merge (target_schema varchar(64),target_merge_table varchar(64))
begin
declare done varchar(5) default 'START';
declare table_name varchar(64);
declare v_table_id int;
declare v_schema_id int;
declare table_list cursor for
select c.table_name
from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id join table_management.dbtables c on b.table_id=c.parent_table_id
where a.schema_name=target_schema and b.table_name=target_merge_table;
declare continue handler for not found set done = 'END';
open table_list;
while done<>'END' do
fetch table_list into table_name;
if done<>'END' then
set @drop_table_stmt=concat('drop table ',target_schema,'.',table_name);
prepare drop_table_stmt from @drop_table_stmt;
execute drop_table_stmt;
end if;
end while;
close table_list;
set @drop_merge_table_stmt=concat('drop table ',target_schema,'.',target_merge_table);
prepare drop_merge_table_stmt from @drop_merge_table_stmt;
execute drop_merge_table_stmt;
start transaction;
select c.table_id,c.schema_id
into v_table_id,v_schema_id
from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id join table_management.dbtables c on b.table_id=c.parent_table_id
where a.schema_name=target_schema and b.table_name=target_merge_table;
delete from table_management.dbtables where table_id=v_table_id and schema_id=v_schema_id;
select a.table_id,a.schema_id
into v_table_id,v_schema_id
from table_management.dbtables a join table_management.dbschemas b on a.schema_id=b.schema_id
where b.schema_name=target_schema and a.table_name=target_merge_table;
delete from table_management.dbtables where table_id=v_table_id and schema_id=v_schema_id;
commit;
deallocate prepare drop_merge_table_stmt;
deallocate prepare drop_table_stmt;
end//
delimiter ;

#################Alter a Merge table and all its underlying MyISAM tables #########################
drop procedure table_management_alter_merge;
delimiter //
create procedure table_management_alter_merge (target_schema varchar(64),alter_stmt varchar(10000))
begin

declare done varchar(5) default 'START';
declare table_name varchar(64);
declare merge_table_name varchar(64);
declare column_def varchar(10000);
declare table_list cursor for
select b.table_name
from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id join table_management.dbtables c on b.table_id=c.parent_table_id
where a.schema_name=target_schema and b.table_name=@merge_table_name
union
select c.table_name
from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id join table_management.dbtables c on b.table_id=c.parent_table_id
where a.schema_name=target_schema and b.table_name=@merge_table_name;
declare continue handler for not found set done = 'END';

select trim(substring(trim(substring(alter_stmt,locate('table',alter_stmt)+5)),1,locate(' ',trim(substring(alter_stmt,locate('table',alter_stmt)+5)))-1)) into @merge_table_name;

select substring(trim(substring(alter_stmt,locate('table',alter_stmt)+5)),locate(' ',trim(substring(alter_stmt,locate('table',alter_stmt)+5)))) into @column_def;

open table_list;
while done<>'END' do
fetch table_list into table_name;
if done<>'END' then
set @alter_table_stmt=concat('alter table ',target_schema,'.',table_name,' ',@column_def);
prepare alter_table_stmt from @alter_table_stmt;
execute alter_table_stmt;
end if;
end while;
close table_list;
deallocate prepare alter_table_stmt;
end//
delimiter ;

#################Create a new MyISAM table and add it to the a Merge table #########################

drop procedure table_management_add_new_table_to_merge;
delimiter //
create procedure table_management_add_new_table_to_merge (target_schema varchar(64),target_merge_table varchar(64),new_table varchar(64))
begin
declare i int default 0;
declare done varchar(5) default 'START';
declare table_name varchar(64);
declare table_union varchar(10000) default '';
declare table_list cursor for
select c.table_name
from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id join table_management.dbtables c on b.table_id=c.parent_table_id
where a.schema_name=target_schema and b.table_name=target_merge_table;
declare continue handler for not found set done = 'END';
open table_list;
while done<>'END' do
fetch table_list into table_name;
if done<>'END' then
if i=0 then
set table_union=concat(target_schema,'.',table_name);
else
set table_union=concat(table_union,',',target_schema,'.',table_name);
end if;
set i=i+1;
end if;
end while;
close table_list;
set @create_table_stmt=concat('create table ',target_schema,'.',new_table,' like ',target_schema,'.',target_merge_table,'_tmpt');
prepare create_table_stmt from @create_table_stmt;
execute create_table_stmt;
set @alter_table_stmt=concat('alter table ',target_schema,'.',target_merge_table,' UNION=(',table_union,',',target_schema,'.',new_table,')');
prepare alter_table_stmt from @alter_table_stmt;
execute alter_table_stmt;
set @insert_into_dbtables_stmt=concat('insert into table_management.dbtables (schema_id,table_name,parent_table_id) select a.schema_id,\'',new_table,'\',b.table_id from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id where a.schema_name=\'',target_schema,'\' and table_name=\'',target_merge_table,'\'');
prepare insert_into_dbtables_stmt from @insert_into_dbtables_stmt;
execute insert_into_dbtables_stmt;
deallocate prepare create_table_stmt;
deallocate prepare alter_table_stmt;
deallocate prepare insert_into_dbtables_stmt;
end//
delimiter ;


#################Take a MyISAM table out of ther Merge and alter the merge accordingly #########################
drop procedure table_management_drop_table_from_merge;
delimiter //
create procedure table_management_drop_table_from_merge (target_schema varchar(64),target_merge_table varchar(64),table_to_drop varchar(64))
begin
declare i int default 0;
declare v_schema_id int;
declare v_table_id int;
declare done varchar(5) default 'START';
declare table_name varchar(64);
declare table_union varchar(10000) default '';
declare table_list cursor for
select c.table_name
from table_management.dbschemas a join table_management.dbtables b on a.schema_id=b.schema_id join table_management.dbtables c on b.table_id=c.parent_table_id
where a.schema_name=target_schema and b.table_name=target_merge_table;
declare continue handler for not found set done = 'END';
open table_list;
while done<>'END' do
fetch table_list into table_name;
if done<>'END' then
if table_name<>table_to_drop then
if i=0 then
set table_union=concat(target_schema,'.',table_name);
else
set table_union=concat(table_union,',',target_schema,'.',table_name);
end if;
set i=i+1;
end if;
end if;
end while;
close table_list;
set @alter_table_stmt=concat('alter table ',target_schema,'.',target_merge_table,' UNION=(',table_union,')');
prepare alter_table_stmt from @alter_table_stmt;
execute alter_table_stmt;
start transaction;
delete table_management.dbtables
from
table_management.dbschemas join
table_management.dbtables on table_management.dbschemas.schema_id=table_management.dbtables.schema_id
where table_management.dbschemas.schema_name=target_schema and table_management.dbtables.table_name=table_to_drop;
commit;
deallocate prepare alter_table_stmt;
end//


3. Create the initial merge and template tables:

create database secure3;
table_management_create_new_merge call ('secure3', 'create table myminutetable (id int, timestamp datetime, primary key (id ))');


4. Create a test event. For this, the event scheduler must be enable. This is disabled by default.

To enable it, do:
SET GLOBAL event_scheduler = ON;
To make the change permanent, Add the following to the my.cnf mysqld section inside the
event_scheduler = ON

To check this do a "show running processlist\G" and you should see something like this:

Id: 1
User: event_scheduler
Host: localhost
db: NULL
Command: Daemon
Time: 5
State: Waiting for next activation
Info: NULL

Now you can create the test event, in this case I am creating an event that runs every minute and adds a new table to merge.

drop event myminutetable;
delimiter //

create event myminutetable
on schedule every 1 minute
do
begin
declare datetime varchar(14) default '';
declare table_name varchar(64) default '';
select date_format(now(),'%Y%m%d%k%i') into datetime;
set @table_name=concat('myminutetable_',datetime);
call table_management_add_new_table_to_merge ('secure3','myminutetable',@table_name);
end//
delimiter ;

Verify that it is creating tables every minute:

use secure3;
show tables;

Verify that table_management is being updated

use table_management;
select * from dbtables;

Future improvements:

1. Exception and error handling
2. Parameter validation