Imas vise opcija za resavanje tog problema.
Jedna je i sleep - to je svakako laksa za implementaciju.
Ali sta ako se server resetuje na pola tvog posla? U nekim slucajevima je to prihvatljivo, a nekada nije ...
Druga opcija je dosta komplikovanija, ali je zato mnogo sigurnija. U teoriji bi to bilo da posao izdelis i odradjujes iz X puta. Podesis cron (scheduled task) da svakih Y sekundi/minuta/sati zove tvoj program. A pri tome pamtis dokle si stigao sa poslom, pa sledeci put nastavis ... tako sto recimo koristis bazu sa transakcijama (MySQL izmedju ostalih podrzavatransakcije ali kada je tabela InnoDB tipa).
Inace obicno se kod web aplikacija desava da sama inicializacija prevodioca bude veci deo posla (PHP, Perl, Ruby, Python ... ). A jos vise ucitavanje kalasa (modula ili kako se vec to zove u svakom od navedenih).
Napisi obican hello world ( print('Hello World'); ) a u drugoj varijanti dodaj i ucitavanje nekih vecih klasa bez da ista radis sa njima, pa sa ab (dolazi uz Apache) benchmarkuj oba... Naravno ako klase i prevodilac nemaju "lazy loading" koji prevodi samo ono sto se koristi u aplikaciji. Neverovatno kolika je razlika.
To naravno ne znaci da klase ne trebaju da se koriste ... glupo je i navoditi razloge zbog kojih ih treba koristiti.
Resenja tipa FastCGI, SGCI (koja rade na mnogim web serverima) ili u slucaju Apache web servera mod_perl, mod_php, mod_python, mod_* ... interpretator "ugradjuje" u sam Apache. Smanjuju opterecenje jer je prevodilac stalno u memoriji - a u nekim resenjima su i klase koje aplikacija koristi samo jednom prevedene na pocetku, kao i same aplikacije. Cak je moguce odrzavati i stalnu konekciju sa bazom u nekim implementacija ...
Pod obicnim CGI-jem, bilo da je u pitanju PHP, Perl, ili exe koji je dobijen kompajliranjem C/C++ koda - dobar deo ode na sam CGI deo.
A sto se interakcije sa bazom tice - mnogo vise "kosta" samo konektovanje nego neki "obican" upit. Naravno to ne vazi za tabele sa zilion rekorda i suludim upitima :)
Za kraj, evo slican primer programcica koji svakih par sati obradjuje neke poruzbine.Nije bas ono sto tebi treba, ali veoma slicno. A ocigledno da je veoma bitno da se neka ne propusti, ili recimo dva puta obradi ...
Pisan je u Perl-u koji je slican sa PHP-om (i ostalim jezicima jer i onako svi sada "pozajmljuju" dobre ideje jedni od drugih).
Code:
#!/usr/bin/perl
#######################################################################################
use lib '/home/reorder1/www/AutoReorder/CPAN/';
use lib '/home/reorder1/www/';
use strict;
use HTML::Template;
use Locale::Currency::Format;
use AutoReorder::Foundation::Email;
use AutoReorder::Foundation::DataBase;
my $DB = AutoReorder::Foundation::DataBase->new('/home/reorder1/www/AutoReorder/Config/databases.conf');
$DB->dbh->{RaiseError} = 1;
$DB->dbh->{PrintError} = 1;
# There is no chance that this will be runned under mod_perl, right?
# Storing those here so we wouldn't make same queries several times
my $customers = {}; # Will hold customers info
########## Get all recurring orders that should be reordered today ##########
my $SQL = <<SQL;
SELECT * FROM recurring_orders WHERE
((start_date <= NOW()) AND (last_reorder IS NULL))
OR
((((UNIX_TIMESTAMP(CURRENT_DATE()) - UNIX_TIMESTAMP(start_date)) / 86400) % frequency) = 0
AND (last_reorder < NOW()));
SQL
my $recurring = $DB->execute(sql => $SQL,
method => 'fetchall_aohref');
########## Get info about all the products ##########
$SQL = <<SQL;
SELECT p.products_id, p.products_price, pd.products_name, tr.tax_rate tax_rate
FROM products p INNER JOIN products_description pd ON p.products_id = pd.products_id
LEFT JOIN tax_class tc ON tc.tax_class_id = p.products_tax_class_id
LEFT JOIN tax_rates tr ON tc.tax_class_id = tr.tax_class_id
WHERE p.products_price > 0 AND p.products_status = 1
SQL
my $products = $DB->execute(sql => $SQL,
database => 'oscommerce')->fetchall_hashref('products_id');
########## Do the things for each found recurring order ##########
foreach my $row (@{$recurring}){
my $items = $DB->execute(
sql => 'SELECT * FROM recurring_items WHERE recurring_id = ?',
data => [$row->{id}],
method => 'fetchall_aohref',);
my $customer_id = $row->{customer_id};
unless(defined $customers->{$customer_id}){
$customers->{$customer_id} =
$DB->execute(sql => 'SELECT * FROM customers WHERE id =?',
data => [$row->{customer_id}])->fetchrow_hashref();
}
## Turn off autocommit
$DB->dbh()->{AutoCommit} = 0;
$DB->dbh()->{RaiseError} = 1;
$DB->dbh()->{PrintError} = 1;
eval {
my $order_id = &make_order($customers->{$customer_id}, $row, $items);
&send_email($order_id);
$DB->execute(sql => 'UPDATE recurring_orders SET last_reorder = CURRENT_DATE()
WHERE id = ?',
data => [$row->{id}]);
##### TRANSACTION END #####
$DB->dbh()->commit();
};
if($@){
# OOOPS! Transaction failed.
eval { $DB->dbh()->rollback() };
print "Couldnt insert/add data", "There is a problem with transaction.";
}
}
print "All recurrig orders for this day processed sucesfully.";
sub make_order {
my ($customer, $recurring, $items) = @_;
require Date::Format;
my $date = Date::Format::time2str('%Y-%m-%d', time);
$DB->insert(table => 'orders',
data => {date => $date,
recurring_id => $recurring->{id},
customer_id => $recurring->{customer_id},
});
my $order_id = $DB->dbh()->{'mysql_insertid'};
foreach my $row (@{$items}){
my $product_id = $row->{product_id};
my $product = $products->{$product_id};
$DB->insert(table => 'order_items',
data => {product_id => $product_id,
order_id => $order_id,
quantity => $row->{quantity},
tax_percentage => ($customer->{pays_tax} ? $product->{tax_rate} : 0),
# If customer doesn't pays tax, set it to 0 ...
price => $product->{products_price}
});
}
return $order_id;
}
sub send_email {
my $order_id = shift;
my $order = $DB->execute(sql => "SELECT id, recurring_id, customer_id, status, comment,
DATE_FORMAT(date, '%m-%d-%Y') as date FROM orders WHERE id = ?",
data => [$order_id],)->fetchrow_hashref();
my $customer = $DB->execute(sql => 'SELECT * FROM customers where id = ?',
data => [$order->{customer_id}],
method => 'fetchall_aohref');
my $STH = $DB->execute(sql => "SELECT *, (price + price * tax_percentage / 100) as 'price_tax'
FROM order_items WHERE order_id = ?",
data => [$order_id]);
my $items;
$order->{grand_total} = 0;
while(my $row = $STH->fetchrow_hashref()){
$row->{sub_total} = $row->{quantity} * $row->{price_tax};
$order->{grand_total} = $order->{grand_total} + $row->{sub_total};
$row->{price} = currency_format('usd', $row->{price}, FMT_SYMBOL);
$row->{tax_percentage} = substr(currency_format('usd', $row->{tax_percentage}, FMT_SYMBOL),1) . '%';
$row->{price_tax} = currency_format('usd', $row->{price_tax}, FMT_SYMBOL);
$row->{sub_total} = currency_format('usd', $row->{sub_total}, FMT_SYMBOL);
$row->{product_name} = $products->{$row->{product_id}}->{products_name};
push (@{$items},$row);
}
$order->{grand_total} = currency_format('usd', $order->{grand_total}, FMT_SYMBOL);
my $template = HTML::Template->new(filename => '/home/reorder1/www/AutoReorder/Templates/Email/order.dwt',
die_on_bad_params => 0);
$template->param(%{$order});
$template->param(customer => $customer);
$template->param(items => $items);
my $mail = AutoReorder::Foundation::Email->new(type => 'html',
subject => 'New Order #' . $order_id,
text => $template->output(),
config_file => '/home/reorder1/www/AutoReorder/Config/email.conf');
return 1;
}
Heh - eto sad sam primetio par stvari koje mogu da budu pomerene van petlji pa bi za koji sekund ubrzale stvari :) Ali u trenutku pisanja nije bila potreba za daljom optimizacijom posto radi zadovoljavajuce ...