summaryrefslogtreecommitdiffstats
path: root/sca-cpp/trunk/components
diff options
context:
space:
mode:
authorjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-08-16 06:15:24 +0000
committerjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-08-16 06:15:24 +0000
commit16f96409b9ad2a1451c88b4e0074b57686f02269 (patch)
tree7c5c2e1d5276975aa26c9056a008cd302306a998 /sca-cpp/trunk/components
parente351502e55d7de56a2d14ad33923f796a73d118f (diff)
Test Postgresql hot standby + replication and integrated Postgresql database in store-cluster sample. Add a front cache component which can be used to wire a cache component and a database component.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@985799 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'sca-cpp/trunk/components')
-rw-r--r--sca-cpp/trunk/components/cache/Makefile.am8
-rw-r--r--sca-cpp/trunk/components/cache/client-test.cpp20
-rw-r--r--sca-cpp/trunk/components/cache/frontcache.cpp125
-rw-r--r--sca-cpp/trunk/components/cache/memcache.composite21
-rw-r--r--sca-cpp/trunk/components/cache/memcache.cpp13
-rw-r--r--sca-cpp/trunk/components/cache/memcache.hpp15
-rwxr-xr-xsca-cpp/trunk/components/cache/server-test6
-rw-r--r--sca-cpp/trunk/components/chat/xmpp.hpp12
-rw-r--r--sca-cpp/trunk/components/nosqldb/nosqldb.cpp13
-rw-r--r--sca-cpp/trunk/components/nosqldb/tinycdb.hpp38
-rw-r--r--sca-cpp/trunk/components/sqldb/Makefile.am11
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql12
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql-backup41
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql-conf105
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql-standby-conf122
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql-standby-testbin0 -> 526293 bytes
-rw-r--r--sca-cpp/trunk/components/sqldb/pgsql-standby-test.cpp88
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql-start12
-rwxr-xr-xsca-cpp/trunk/components/sqldb/pgsql-stop3
-rw-r--r--sca-cpp/trunk/components/sqldb/pgsql-test.cpp4
-rw-r--r--sca-cpp/trunk/components/sqldb/pgsql.hpp79
-rwxr-xr-xsca-cpp/trunk/components/sqldb/server-test9
-rwxr-xr-xsca-cpp/trunk/components/sqldb/sqldb-test1
-rw-r--r--sca-cpp/trunk/components/sqldb/sqldb.composite2
-rw-r--r--sca-cpp/trunk/components/sqldb/sqldb.cpp13
-rwxr-xr-xsca-cpp/trunk/components/sqldb/standby-test39
26 files changed, 681 insertions, 131 deletions
diff --git a/sca-cpp/trunk/components/cache/Makefile.am b/sca-cpp/trunk/components/cache/Makefile.am
index 580a5e4d84..96fb476fef 100644
--- a/sca-cpp/trunk/components/cache/Makefile.am
+++ b/sca-cpp/trunk/components/cache/Makefile.am
@@ -27,12 +27,18 @@ memcached.prefix: $(top_builddir)/config.status
EXTRA_DIST = memcache.composite
-comp_LTLIBRARIES = libmemcache.la
+comp_LTLIBRARIES = libmemcache.la libfrontcache.la
+
libmemcache_la_SOURCES = memcache.cpp
noinst_DATA = libmemcache.so
libmemcache.so:
ln -s .libs/libmemcache.so
+libfrontcache_la_SOURCES = frontcache.cpp
+noinst_DATA = libfrontcache.so
+libfrontcache.so:
+ ln -s .libs/libfrontcache.so
+
memcache_test_SOURCES = memcache-test.cpp
memcache_test_LDFLAGS = -lxml2
diff --git a/sca-cpp/trunk/components/cache/client-test.cpp b/sca-cpp/trunk/components/cache/client-test.cpp
index 25bdb722f3..4f655c3d98 100644
--- a/sca-cpp/trunk/components/cache/client-test.cpp
+++ b/sca-cpp/trunk/components/cache/client-test.cpp
@@ -36,9 +36,10 @@
namespace tuscany {
namespace cache {
-const string uri("http://localhost:8090/memcache");
+const string memcacheuri("http://localhost:8090/memcache");
+const string frontcacheuri("http://localhost:8090/frontcache");
-bool testCache() {
+bool testCache(const string& uri) {
http::CURLSession cs;
const list<value> i = list<value>()
@@ -84,6 +85,14 @@ bool testCache() {
return true;
}
+bool testMemcache() {
+ return testCache(memcacheuri);
+}
+
+bool testFrontcache() {
+ return testCache(frontcacheuri);
+}
+
struct getLoop {
const string path;
const value entry;
@@ -91,7 +100,7 @@ struct getLoop {
getLoop(const string& path, const value& entry, http::CURLSession cs) : path(path), entry(entry), cs(cs) {
}
const bool operator()() const {
- const failable<value> val = http::get(uri + path, cs);
+ const failable<value> val = http::get(memcacheuri + path, cs);
assert(hasContent(val));
assert(content(val) == entry);
return true;
@@ -105,7 +114,7 @@ bool testGetPerf() {
const value a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
http::CURLSession cs;
- const failable<value> id = http::post(a, uri, cs);
+ const failable<value> id = http::post(a, memcacheuri, cs);
assert(hasContent(id));
const string p = path(content(id));
@@ -121,7 +130,8 @@ bool testGetPerf() {
int main() {
tuscany::cout << "Testing..." << tuscany::endl;
- tuscany::cache::testCache();
+ tuscany::cache::testMemcache();
+ tuscany::cache::testFrontcache();
tuscany::cache::testGetPerf();
tuscany::cout << "OK" << tuscany::endl;
diff --git a/sca-cpp/trunk/components/cache/frontcache.cpp b/sca-cpp/trunk/components/cache/frontcache.cpp
new file mode 100644
index 0000000000..a9b18f0792
--- /dev/null
+++ b/sca-cpp/trunk/components/cache/frontcache.cpp
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * A front cache component implementation which coordinates access to two
+ * levels of backend read/write caches or stores. Each cache or store is
+ * accessed through two references: a writer reference and a reader reference.
+ *
+ * This is useful if your level2 store is made of a master and slave
+ * replicated databases, you can then wire the writer reference to the master
+ * database and the reader reference to one your slave databases (assuming
+ * that the updates eventually get replicated to the slave database,in the
+ * meantime the updates will be retrieved from the level1 cache).
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace frontcache {
+
+/**
+ * Get an item from the cache.
+ */
+const failable<value> get(const value& key, const lambda<value(const list<value>&)> rcache1, const lambda<value(const list<value>&)> wcache1, const lambda<value(const list<value>&)> rcache2, unused const lambda<value(const list<value>&)> wcache2) {
+
+ // Lookup level1 cache
+ const value val1 = rcache1(mklist<value>("get", key));
+ if (!isNil(val1))
+ return val1;
+
+ // Lookup level2 cache
+ const value val2 = rcache2(mklist<value>("get", key));
+ if (isNil(val2))
+ return mkfailure<value>("Couldn't get cache entry");
+
+ // Update level1 cache
+ wcache1(mklist<value>("put", key, val2));
+
+ return val2;
+}
+
+/**
+ * Post an item to the cache.
+ */
+const failable<value> post(const value& key, const value& val, unused const lambda<value(const list<value>&)> rcache1, const lambda<value(const list<value>&)> wcache1, unused const lambda<value(const list<value>&)> rcache2, const lambda<value(const list<value>&)> wcache2) {
+ const value id = append<value>(key, mklist(mkuuid()));
+
+ // Update level1 cache
+ wcache1(mklist<value>("put", id, val));
+
+ // Update level2 cache
+ wcache2(mklist<value>("put", id, val));
+
+ return id;
+}
+
+/**
+ * Put an item into the cache.
+ */
+const failable<value> put(const value& key, const value& val, unused const lambda<value(const list<value>&)> rcache1, const lambda<value(const list<value>&)> wcache1, unused const lambda<value(const list<value>&)> rcache2, const lambda<value(const list<value>&)> wcache2) {
+
+ // Update level1 cache
+ wcache1(mklist<value>("put", key, val));
+
+ // Update level2 cache
+ wcache2(mklist<value>("put", key, val));
+
+ return value(true);
+}
+
+/**
+ * Delete an item from the cache.
+ */
+const failable<value> del(const value& key, unused const lambda<value(const list<value>&)> rcache1, const lambda<value(const list<value>&)> wcache1, unused const lambda<value(const list<value>&)> rcache2, const lambda<value(const list<value>&)> wcache2) {
+
+ // Delete from level1 cache
+ wcache1(mklist<value>("delete", key));
+
+ // Delete from level2 cache
+ wcache2(mklist<value>("delete", key));
+
+ return value(true);
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "get")
+ return tuscany::frontcache::get(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params));
+ if (func == "post")
+ return tuscany::frontcache::post(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params), caddddddr(params));
+ if (func == "put")
+ return tuscany::frontcache::put(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params), caddddddr(params));
+ if (func == "delete")
+ return tuscany::frontcache::del(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/trunk/components/cache/memcache.composite b/sca-cpp/trunk/components/cache/memcache.composite
index 7c160172f0..4d546db410 100644
--- a/sca-cpp/trunk/components/cache/memcache.composite
+++ b/sca-cpp/trunk/components/cache/memcache.composite
@@ -28,6 +28,25 @@
<t:binding.http uri="memcache"/>
</service>
<property name="servers">localhost,localhost:11212,localhost:11213</property>
- </component>
+ </component>
+
+ <component name="l2cache">
+ <implementation.cpp path="." library="libmemcache"/>
+ <service name="l2cache">
+ <t:binding.http uri="l2cache"/>
+ </service>
+ <property name="servers">localhost:11411,localhost:11412,localhost:11413</property>
+ </component>
+
+ <component name="frontcache">
+ <implementation.cpp path="." library="libfrontcache"/>
+ <service name="frontcache">
+ <t:binding.http uri="frontcache"/>
+ </service>
+ <reference name="l1reader" target="memcache"/>
+ <reference name="l1writer" target="memcache"/>
+ <reference name="l2reader" target="l2cache"/>
+ <reference name="l2writer" target="l2cache"/>
+ </component>
</composite>
diff --git a/sca-cpp/trunk/components/cache/memcache.cpp b/sca-cpp/trunk/components/cache/memcache.cpp
index ec61ae9a92..4b62ce2e4c 100644
--- a/sca-cpp/trunk/components/cache/memcache.cpp
+++ b/sca-cpp/trunk/components/cache/memcache.cpp
@@ -23,10 +23,7 @@
* Memcached-based cache component implementation.
*/
-#include <apr_uuid.h>
-
#include "string.hpp"
-
#include "function.hpp"
#include "list.hpp"
#include "value.hpp"
@@ -46,16 +43,8 @@ const failable<value> get(const list<value>& params, memcache::MemCached& ch) {
/**
* Post an item to the cache.
*/
-const value uuidValue() {
- apr_uuid_t uuid;
- apr_uuid_get(&uuid);
- char buf[APR_UUID_FORMATTED_LENGTH];
- apr_uuid_format(buf, &uuid);
- return value(string(buf, APR_UUID_FORMATTED_LENGTH));
-}
-
const failable<value> post(const list<value>& params, memcache::MemCached& ch) {
- const value id = append<value>(car(params), mklist(uuidValue()));
+ const value id = append<value>(car(params), mklist(mkuuid()));
const failable<bool> val = memcache::post(id, cadr(params), ch);
if (!hasContent(val))
return mkfailure<value>(reason(val));
diff --git a/sca-cpp/trunk/components/cache/memcache.hpp b/sca-cpp/trunk/components/cache/memcache.hpp
index 8749f845bf..1c71b220c7 100644
--- a/sca-cpp/trunk/components/cache/memcache.hpp
+++ b/sca-cpp/trunk/components/cache/memcache.hpp
@@ -91,10 +91,10 @@ private:
apr_memcache_server_t *server;
const apr_status_t sc = apr_memcache_server_create(pool, c_str(host), (apr_port_t)port, 1, 1, 1, 600, &server);
if (sc != APR_SUCCESS)
- return mkfailure<bool>("Could not create server");
+ return mkfailure<bool>("Could not create memcached server");
const apr_status_t as = apr_memcache_add_server(mc, server);
if (as != APR_SUCCESS)
- return mkfailure<bool>("Could not add server");
+ return mkfailure<bool>("Could not add memcached server");
return true;
}
@@ -131,7 +131,7 @@ const failable<bool> post(const value& key, const value& val, const MemCached& c
const string vs(scheme::writeValue(val));
const apr_status_t rc = apr_memcache_add(cache.mc, nospaces(c_str(ks)), const_cast<char*>(c_str(vs)), length(vs), 0, 27);
if (rc != APR_SUCCESS)
- return mkfailure<bool>("Could not add entry");
+ return mkfailure<bool>("Could not add memcached entry");
debug(true, "memcache::post::result");
return true;
@@ -148,7 +148,7 @@ const failable<bool> put(const value& key, const value& val, const MemCached& ca
const string vs(scheme::writeValue(val));
const apr_status_t rc = apr_memcache_set(cache.mc, nospaces(c_str(ks)), const_cast<char*>(c_str(vs)), length(vs), 0, 27);
if (rc != APR_SUCCESS)
- return mkfailure<bool>("Could not set entry");
+ return mkfailure<bool>("Could not set memcached entry");
debug(true, "memcache::put::result");
return true;
@@ -164,9 +164,8 @@ const failable<value> get(const value& key, const MemCached& cache) {
char *data;
apr_size_t size;
const apr_status_t rc = apr_memcache_getp(cache.mc, cache.pool, nospaces(c_str(ks)), &data, &size, NULL);
- if (rc != APR_SUCCESS) {
- return mkfailure<value>("Could not get entry");
- }
+ if (rc != APR_SUCCESS)
+ return mkfailure<value>("Could not get memcached entry");
const value val(scheme::readValue(string(data, size)));
debug(val, "memcache::get::result");
@@ -182,7 +181,7 @@ const failable<bool> del(const value& key, const MemCached& cache) {
const string ks(scheme::writeValue(key));
const apr_status_t rc = apr_memcache_delete(cache.mc, nospaces(c_str(ks)), 0);
if (rc != APR_SUCCESS)
- return mkfailure<bool>("Could not delete entry");
+ return mkfailure<bool>("Could not delete memcached entry");
debug(true, "memcache::delete::result");
return true;
diff --git a/sca-cpp/trunk/components/cache/server-test b/sca-cpp/trunk/components/cache/server-test
index 598d4bce5b..a93a7e73ac 100755
--- a/sca-cpp/trunk/components/cache/server-test
+++ b/sca-cpp/trunk/components/cache/server-test
@@ -29,6 +29,9 @@ EOF
./memcached-start 11211
./memcached-start 11212
./memcached-start 11213
+./memcached-start 11411
+./memcached-start 11412
+./memcached-start 11413
../../modules/http/httpd-start tmp
sleep 2
@@ -41,5 +44,8 @@ rc=$?
./memcached-stop 11211
./memcached-stop 11212
./memcached-stop 11213
+./memcached-stop 11411
+./memcached-stop 11412
+./memcached-stop 11413
sleep 2
return $rc
diff --git a/sca-cpp/trunk/components/chat/xmpp.hpp b/sca-cpp/trunk/components/chat/xmpp.hpp
index 34ab13ed98..a921b9806d 100644
--- a/sca-cpp/trunk/components/chat/xmpp.hpp
+++ b/sca-cpp/trunk/components/chat/xmpp.hpp
@@ -26,8 +26,6 @@
* XMPP support functions.
*/
-#include <apr_uuid.h>
-
#include "strophe.h"
extern "C" {
#include "common.h"
@@ -64,18 +62,10 @@ private:
/**
* Represents an XMPP client.
*/
-const string resourceUUID() {
- apr_uuid_t uuid;
- apr_uuid_get(&uuid);
- char buf[APR_UUID_FORMATTED_LENGTH];
- apr_uuid_format(buf, &uuid);
- return string(buf, APR_UUID_FORMATTED_LENGTH);
-}
-
class XMPPClient {
public:
XMPPClient(const string& jid, const string& pass, bool owner = true) : owner(owner), ctx(xmpp_ctx_new(NULL, xmppRuntime.log)), conn(xmpp_conn_new(ctx)), connecting(false), connected(false), disconnecting(false) {
- xmpp_conn_set_jid(conn, c_str(jid + "/" + resourceUUID()));
+ xmpp_conn_set_jid(conn, c_str(jid + "/" + mkuuid()));
xmpp_conn_set_pass(conn, c_str(pass));
}
diff --git a/sca-cpp/trunk/components/nosqldb/nosqldb.cpp b/sca-cpp/trunk/components/nosqldb/nosqldb.cpp
index 638434c26f..cda3ca44a9 100644
--- a/sca-cpp/trunk/components/nosqldb/nosqldb.cpp
+++ b/sca-cpp/trunk/components/nosqldb/nosqldb.cpp
@@ -23,10 +23,7 @@
* TinyCDB-based database component implementation.
*/
-#include <apr_uuid.h>
-
#include "string.hpp"
-
#include "function.hpp"
#include "list.hpp"
#include "value.hpp"
@@ -46,16 +43,8 @@ const failable<value> get(const list<value>& params, tinycdb::TinyCDB& cdb) {
/**
* Post an item to the database.
*/
-const value uuidValue() {
- apr_uuid_t uuid;
- apr_uuid_get(&uuid);
- char buf[APR_UUID_FORMATTED_LENGTH];
- apr_uuid_format(buf, &uuid);
- return value(string(buf, APR_UUID_FORMATTED_LENGTH));
-}
-
const failable<value> post(const list<value>& params, tinycdb::TinyCDB& cdb) {
- const value id = append<value>(car(params), mklist(uuidValue()));
+ const value id = append<value>(car(params), mklist(mkuuid()));
const failable<bool> val = tinycdb::post(id, cadr(params), cdb);
if (!hasContent(val))
return mkfailure<value>(reason(val));
diff --git a/sca-cpp/trunk/components/nosqldb/tinycdb.hpp b/sca-cpp/trunk/components/nosqldb/tinycdb.hpp
index 143b3da308..c98f2c38da 100644
--- a/sca-cpp/trunk/components/nosqldb/tinycdb.hpp
+++ b/sca-cpp/trunk/components/nosqldb/tinycdb.hpp
@@ -141,13 +141,13 @@ const failable<int> cdbopen(TinyCDB& cdb) {
struct stat st;
const int s = stat(c_str(cdb.name), &st);
if (s == -1)
- return mkfailure<int>(string("Couldn't read database stat ") + cdb.name);
+ return mkfailure<int>(string("Couldn't tinycdb read database stat ") + cdb.name);
// Open database for the first time
if (cdb.fd == -1) {
cdb.fd = open(c_str(cdb.name), O_RDONLY);
if (cdb.fd == -1)
- return mkfailure<int>(string("Couldn't open database file ") + cdb.name);
+ return mkfailure<int>(string("Couldn't open tinycdb database file ") + cdb.name);
debug(cdb.fd, "tinycdb::open::fd");
cdb.st = st;
return cdb.fd;
@@ -162,7 +162,7 @@ const failable<int> cdbopen(TinyCDB& cdb) {
// Reopen database
const int newfd = open(c_str(cdb.name), O_RDONLY);
if (newfd == -1)
- return mkfailure<int>(string("Couldn't open database file ") + cdb.name);
+ return mkfailure<int>(string("Couldn't open tinycdb database file ") + cdb.name);
if (newfd == cdb.fd) {
debug(cdb.fd, "tinycdb::open::fd");
cdb.st = st;
@@ -171,7 +171,7 @@ const failable<int> cdbopen(TinyCDB& cdb) {
// We got a different fd, dup it to the current fd then close it
if (fcntl(newfd, F_DUPFD, cdb.fd) == -1)
- return mkfailure<int>(string("Couldn't dup database file handle ") + cdb.name);
+ return mkfailure<int>(string("Couldn't dup tinycdb database file handle ") + cdb.name);
close(newfd);
debug(cdb.fd, "tinycdb::open::fd");
@@ -212,9 +212,9 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
// Read the db header
unsigned int pos = 0;
if (lseek(fd, 0, SEEK_SET) != 0)
- return mkfailure<bool>("Could not seek to database start");
+ return mkfailure<bool>("Could not seek to tinycdb database start");
if (::read(fd, buf, 2048) != 2048)
- return mkfailure<bool>("Could not read database header");
+ return mkfailure<bool>("Could not read tinycdb database header");
pos += 2048;
unsigned int eod = cdb_unpack(buf);
debug(pos, "tinycdb::rewrite::eod");
@@ -222,9 +222,9 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
// Read and add the existing entries
while(pos < eod) {
if (eod - pos < 8)
- return mkfailure<bool>("Invalid database format, couldn't read entry header");
+ return mkfailure<bool>("Invalid tinycdb database format, couldn't read entry header");
if (::read(fd, buf, 8) != 8)
- return mkfailure<bool>("Couldn't read entry header");
+ return mkfailure<bool>("Couldn't read tinycdb entry header");
pos += 8;
unsigned int klen = cdb_unpack(buf);
unsigned int vlen = cdb_unpack(((unsigned char*)buf) + 4);
@@ -233,9 +233,9 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
// Read existing entry
buf = mkbuffer(buf, elen);
if (eod - pos < elen)
- return mkfailure<bool>("Invalid database format, couldn't read entry");
+ return mkfailure<bool>("Invalid tinycdb database format, couldn't read entry");
if ((unsigned int)::read(fd, buf, elen) != elen)
- return mkfailure<bool>("Couldn't read entry");
+ return mkfailure<bool>("Couldn't read tinycdb entry");
pos += elen;
// Apply the update function to the entry
@@ -251,10 +251,10 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
// Add the entry to the new db
if (cdb_make_add(&cdbm, buf, klen, ((unsigned char*)buf)+klen, vlen) == -1)
- return mkfailure<bool>("Could not add entry");
+ return mkfailure<bool>("Could not add tinycdb entry");
}
if (pos != eod)
- return mkfailure<bool>("Invalid database format");
+ return mkfailure<bool>("Invalid tinycdb database format");
// Call the finish function
const failable<bool> f = finish(cdbm);
@@ -263,7 +263,7 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
// Save the new db
if (cdb_make_finish(&cdbm) == -1)
- return mkfailure<bool>("Could not save database");
+ return mkfailure<bool>("Could not save tinycdb database");
return true;
}
@@ -274,7 +274,7 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
string tmpname = dbname(cdb) + ".XXXXXX";
int tmpfd = mkstemp(const_cast<char*>(c_str(tmpname)));
if (tmpfd == -1)
- return mkfailure<bool>("Could not create temporary database");
+ return mkfailure<bool>("Could not create temporary tinycdb database");
// Rewrite the db, apply the update function to each entry
buffer buf = mkbuffer(2048);
@@ -287,7 +287,7 @@ const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsi
// Atomically replace the db and reopen it in read mode
if (rename(c_str(tmpname), c_str(dbname(cdb))) == -1)
- return mkfailure<bool>("Could not rename temporary database");
+ return mkfailure<bool>("Could not rename temporary tinycdb database");
cdbclose(cdb);
failable<int> ffd = cdbopen(cdb);
if (!hasContent(ffd))
@@ -305,7 +305,7 @@ struct postUpdate {
}
const failable<bool> operator()(buffer& buf, const unsigned int klen, unused const unsigned int vlen) const {
if (ks == string((char*)buf, klen))
- return mkfailure<bool>("Key already exists");
+ return mkfailure<bool>("Key already exists in tinycdb database");
return true;
}
};
@@ -317,7 +317,7 @@ struct postFinish {
}
const failable<bool> operator()(struct cdb_make& cdbm) const {
if (cdb_make_add(&cdbm, c_str(ks), length(ks), c_str(vs), length(vs)) == -1)
- return mkfailure<bool>("Could not add entry");
+ return mkfailure<bool>("Could not add tinycdb entry");
return true;
}
};
@@ -363,7 +363,7 @@ struct putFinish {
}
const failable<bool> operator()(struct cdb_make& cdbm) const {
if (cdb_make_add(&cdbm, c_str(ks), length(ks), c_str(vs), length(vs)) == -1)
- return mkfailure<bool>("Could not add entry");
+ return mkfailure<bool>("Could not add tinycdb entry");
return true;
}
};
@@ -404,7 +404,7 @@ const failable<value> get(const value& key, TinyCDB& cdb) {
cdbi_t vlen;
if (cdb_seek(fd, c_str(ks), length(ks), &vlen) <= 0)
- return mkfailure<value>("Could not get entry");
+ return mkfailure<value>("Could not get tinycdb entry");
char* data = gc_cnew(vlen + 1);
cdb_bread(fd, data, vlen);
data[vlen] = '\0';
diff --git a/sca-cpp/trunk/components/sqldb/Makefile.am b/sca-cpp/trunk/components/sqldb/Makefile.am
index e5c03f5190..7ed24e712c 100644
--- a/sca-cpp/trunk/components/sqldb/Makefile.am
+++ b/sca-cpp/trunk/components/sqldb/Makefile.am
@@ -22,7 +22,7 @@ INCLUDES = -I${PGSQL_INCLUDE}
incl_HEADERS = *.hpp
incldir = $(prefix)/include/components/sqldb
-dist_comp_SCRIPTS = pgsql-start pgsql-stop pgsql
+dist_comp_SCRIPTS = pgsql-conf pgsql-start pgsql-stop pgsql pgsql-standby-conf
compdir=$(prefix)/components/sqldb
comp_DATA = pgsql.prefix
@@ -42,11 +42,14 @@ libsqldb.so:
pgsql_test_SOURCES = pgsql-test.cpp
pgsql_test_LDFLAGS = -L${PGSQL_LIB} -R${PGSQL_LIB} -lpq
+pgsql_standby_test_SOURCES = pgsql-standby-test.cpp
+pgsql_standby_test_LDFLAGS = -L${PGSQL_LIB} -R${PGSQL_LIB} -lpq
+
client_test_SOURCES = client-test.cpp
client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
-dist_noinst_SCRIPTS = sqldb-test server-test
-noinst_PROGRAMS = pgsql-test client-test
-TESTS = sqldb-test server-test
+dist_noinst_SCRIPTS = sqldb-test standby-test server-test
+noinst_PROGRAMS = pgsql-test pgsql-standby-test client-test
+TESTS = sqldb-test standby-test server-test
endif
diff --git a/sca-cpp/trunk/components/sqldb/pgsql b/sca-cpp/trunk/components/sqldb/pgsql
index 3cd1904e32..dab30e642b 100755
--- a/sca-cpp/trunk/components/sqldb/pgsql
+++ b/sca-cpp/trunk/components/sqldb/pgsql
@@ -21,5 +21,15 @@
here=`readlink -f $0`; here=`dirname $here`
pgsql_prefix=`cat $here/pgsql.prefix`
-$pgsql_prefix/bin/psql -c "$1" db
+if [ "$2" = "" ]; then
+ host="localhost"
+ port="5432"
+ cmd="$1"
+else
+ host="$1"
+ port="$2"
+ cmd="$3"
+fi
+
+$pgsql_prefix/bin/psql -h $host -p $port -c "$cmd" db
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-backup b/sca-cpp/trunk/components/sqldb/pgsql-backup
new file mode 100755
index 0000000000..fad59236bf
--- /dev/null
+++ b/sca-cpp/trunk/components/sqldb/pgsql-backup
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Backup postgresql data directory
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+if [ "$2" = "" ]; then
+ host="localhost"
+ port="5432"
+else
+ host="$2"
+ port="$3"
+fi
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+$pgsql_prefix/bin/psql -h $host -p $port -c "SELECT pg_start_backup('backup', true)" db 1>>$root/logs/postgresql 2>&1
+
+echo "Content-type: application/x-compressed"
+echo
+
+tar -C $root/sqldb -cz data
+
+$pgsql_prefix/bin/psql -h $host -p $port -c "SELECT pg_stop_backup()" db 1>>$root/logs/postgresql 2>&1
+
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-conf b/sca-cpp/trunk/components/sqldb/pgsql-conf
new file mode 100755
index 0000000000..f5cc2d23e3
--- /dev/null
+++ b/sca-cpp/trunk/components/sqldb/pgsql-conf
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Configure a postgresql master server
+here=`readlink -f $0`; here=`dirname $here`
+mkdir -p $1
+root=`readlink -f $1`
+
+addr=$2
+if [ "$addr" = "" ]; then
+ ip="*"
+ port="5432"
+else
+ ip=`$here/../../modules/http/httpd-addr ip $addr`
+ if [ "$ip" = "" ]; then
+ ip="*"
+ fi
+ port=`$here/../../modules/http/httpd-addr port $addr`
+fi
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+mkdir -p $root/sqldb/data
+chmod 700 $root/sqldb/data
+mkdir -p $root/sqldb/archive
+mkdir -p $root/logs
+if [ ! -f $root/sqldb/data/postgresql.conf ]; then
+ $pgsql_prefix/bin/pg_ctl init -D $root/sqldb/data 1>>$root/logs/postgresql 2>&1
+ cp $root/sqldb/data/postgresql.conf $root/sqldb/data/postgresql-init.conf
+ cp $root/sqldb/data/pg_hba.conf $root/sqldb/data/pg_hba-init.conf
+fi
+
+# Generate server configuration
+cp $root/sqldb/data/postgresql-init.conf $root/sqldb/data/postgresql.conf
+cat >>$root/sqldb/data/postgresql.conf <<EOF
+
+# Generated by: pgsql-conf $*
+
+# Listen
+listen_addresses = '$ip'
+port = $port
+
+# Setup archival
+archive_mode = on
+archive_command = 'cp %p $root/sqldb/archive/%f'
+
+# Setup hot standby with streaming replication
+wal_level = hot_standby
+max_wal_senders = 5
+wal_keep_segments = 32
+
+EOF
+
+# Generate client auth configuration
+cp $root/sqldb/data/pg_hba-init.conf $root/sqldb/data/pg_hba.conf
+cat >>$root/sqldb/data/pg_hba.conf <<EOF
+
+# Generated by: pgsql-conf $*
+# TYPE DATABASE USER CIDR-ADDRESS METHOD
+host all all samenet trust
+host replication all samenet trust
+
+EOF
+
+# Create the db
+$pgsql_prefix/bin/pg_ctl start -w -D $root/sqldb/data -l $root/logs/postgresql 1>>$root/logs/postgresql 2>&1
+$pgsql_prefix/bin/createdb -h localhost -p $port db 1>>$root/logs/postgresql 2>&1
+$pgsql_prefix/bin/pg_ctl stop -w -D $root/sqldb/data 1>>$root/logs/postgresql 2>&1
+
+# Generate database backup script
+mkdir -p $root/sqldb/scripts
+cat >$root/sqldb/scripts/backup <<EOF
+#!/bin/sh
+$here/pgsql-backup $root localhost $port
+EOF
+chmod 700 $root/sqldb/scripts/backup
+
+# Configure HTTPD to serve backup and archive files
+if [ -f "$root/conf/httpd.conf" ]; then
+ cat >>$root/conf/httpd.conf <<EOF
+# Generated by: pgsql-conf $*
+
+# Serve PostgreSQL backup and WAL archive files
+ScriptAlias /pgsql-backup "$root/sqldb/scripts/backup"
+Alias /pgsql-archive "$root/sqldb/archive"
+
+EOF
+
+fi
+
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-standby-conf b/sca-cpp/trunk/components/sqldb/pgsql-standby-conf
new file mode 100755
index 0000000000..3b4aa6dff5
--- /dev/null
+++ b/sca-cpp/trunk/components/sqldb/pgsql-standby-conf
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Configure a postgresql hot standby server
+here=`readlink -f $0`; here=`dirname $here`
+mkdir -p $1
+root=`readlink -f $1`
+
+# Master server address
+if [ "$2" = "" ]; then
+ mhost="localhost"
+ mport="5432"
+ mhttpport="80"
+else
+ mhost="$2"
+ mport="$3"
+ mhttpport="$4"
+fi
+
+# Server address
+addr=$5
+if [ "$addr" = "" ]; then
+ ip="*"
+ port="5432"
+else
+ ip=`$here/../../modules/http/httpd-addr ip $addr`
+ if [ "$ip" = "" ]; then
+ ip="*"
+ fi
+ port=`$here/../../modules/http/httpd-addr port $addr`
+fi
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+mkdir -p $root/sqldb/data
+chmod 700 $root/sqldb/data
+mkdir -p $root/sqldb/archive
+mkdir -p $root/logs
+
+# Initialize from a backup of the master
+if [ ! -f $root/sqldb/data/postgresql.conf ]; then
+ (wget http://$mhost:$mhttpport/pgsql-backup -O - | tar -C $root/sqldb -xz) 1>>$root/logs/postgresql 2>&1
+ rm -rf $root/sqldb/data/postmaster.pid $root/sqldb/data/pg_xlog
+ mkdir -p $root/sqldb/data/pg_xlog/archive_status
+ chmod 700 $root/sqldb/data/pg_xlog/archive_status
+fi
+
+# Generate server configuration
+cp $root/sqldb/data/postgresql-init.conf $root/sqldb/data/postgresql.conf
+cat >>$root/sqldb/data/postgresql.conf <<EOF
+
+# Generated by: standby-conf $*
+
+# Listen
+listen_addresses = '$ip'
+port = $port
+
+# Setup archival
+archive_mode = on
+archive_command = 'cp %p $root/sqldb/archive/%f'
+
+# Setup hot standby with streaming replication
+wal_level = hot_standby
+max_wal_senders = 5
+wal_keep_segments = 32
+
+# Enable hot standby
+hot_standby = on
+
+EOF
+
+# Generate recovery configuration
+cat >$root/sqldb/data/recovery.conf << EOF
+# Generated by: pgsql-slave-conf $*
+
+# Start in standby mode
+standby_mode = 'on'
+primary_conninfo = 'host=$mhost port=$mport'
+
+# Failover
+trigger_file = '$root/sqldb/failover'
+
+restore_command = 'wget http://$mhost:$mhttpport/pgsql-archive/%f -O "%p"'
+
+EOF
+
+# Generate database backup script
+mkdir -p $root/sqldb/scripts
+cat >$root/sqldb/scripts/backup <<EOF
+#!/bin/sh
+$here/pgsql-backup $root localhost $port
+EOF
+chmod 700 $root/sqldb/scripts/backup
+
+# Configure HTTPD to serve backup and archive files
+if [ -f "$root/conf/httpd.conf" ]; then
+ cat >>$root/conf/httpd.conf <<EOF
+# Generated by: pgsql-conf $*
+
+# Serve PostgreSQL backup and WAL archive files
+ScriptAlias /pgsql-backup "$root/sqldb/scripts/backup"
+Alias /pgsql-archive "$root/sqldb/archive"
+
+EOF
+
+fi
+
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-standby-test b/sca-cpp/trunk/components/sqldb/pgsql-standby-test
new file mode 100755
index 0000000000..bf6b643c50
--- /dev/null
+++ b/sca-cpp/trunk/components/sqldb/pgsql-standby-test
Binary files differ
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-standby-test.cpp b/sca-cpp/trunk/components/sqldb/pgsql-standby-test.cpp
new file mode 100644
index 0000000000..44f0a4a9e6
--- /dev/null
+++ b/sca-cpp/trunk/components/sqldb/pgsql-standby-test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test PostgreSQL hot standby support.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "pgsql.hpp"
+
+namespace tuscany {
+namespace pgsql {
+
+bool testPGSql() {
+ PGSql wpg("host=localhost port=5432 dbname=db", "test");
+ PGSql rpg("host=localhost port=5433 dbname=db", "test");
+ const value k = mklist<value>("a");
+
+ assert(hasContent(post(k, string("AAA"), wpg)));
+ sleep(1);
+ assert((get(k, rpg)) == value(string("AAA")));
+ assert(hasContent(put(k, string("aaa"), wpg)));
+ sleep(1);
+ assert((get(k, rpg)) == value(string("aaa")));
+ assert(hasContent(del(k, wpg)));
+ sleep(1);
+ assert(!hasContent(get(k, rpg)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ PGSql& pg;
+ getLoop(const value& k, PGSql& pg) : k(k), pg(pg) {
+ }
+ const bool operator()() const {
+ assert((get(k, pg)) == value(string("CCC")));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const value k = mklist<value>("c");
+ PGSql wpg("host=localhost port=5432 dbname=db", "test");
+ PGSql rpg("host=localhost port=5433 dbname=db", "test");
+ assert(hasContent(post(k, string("CCC"), wpg)));
+ sleep(1);
+
+ const lambda<bool()> gl = getLoop(k, rpg);
+ cout << "PGSql get test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::pgsql::testPGSql();
+ tuscany::pgsql::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-start b/sca-cpp/trunk/components/sqldb/pgsql-start
index f5c0f87614..3f03d0b4dc 100755
--- a/sca-cpp/trunk/components/sqldb/pgsql-start
+++ b/sca-cpp/trunk/components/sqldb/pgsql-start
@@ -24,14 +24,6 @@ root=`readlink -f $1`
pgsql_prefix=`cat $here/pgsql.prefix`
mkdir -p $root/sqldb
mkdir -p $root/logs
-if [ ! -f $root/sqldb/postgresql.conf ]; then
- $pgsql_prefix/bin/pg_ctl init -D $root/sqldb 1>/dev/null 2>&1
- createdb="true"
-fi
-
-$pgsql_prefix/bin/pg_ctl start -D $root/sqldb -l $root/logs/postgresql 1>/dev/null 2>&1
-sleep 2
-if [ "$createdb" = "true" ]; then
- $pgsql_prefix/bin/createdb db 1>/dev/null 2>&1
-fi
+$pgsql_prefix/bin/pg_ctl start -w -D $root/sqldb/data -l $root/logs/postgresql 1>>$root/logs/postgresql 2>&1
+sleep 1
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-stop b/sca-cpp/trunk/components/sqldb/pgsql-stop
index d0cda096ba..eefade47d2 100755
--- a/sca-cpp/trunk/components/sqldb/pgsql-stop
+++ b/sca-cpp/trunk/components/sqldb/pgsql-stop
@@ -22,7 +22,6 @@ here=`readlink -f $0`; here=`dirname $here`
root=`readlink -f $1`
pgsql_prefix=`cat $here/pgsql.prefix`
-mkdir -p $root/sqldb
mkdir -p $root/logs
-$pgsql_prefix/bin/pg_ctl stop -D $root/sqldb 1>/dev/null 2>&1
+$pgsql_prefix/bin/pg_ctl stop -w -D $root/sqldb/data 1>>$root/logs/postgresql 2>&1
diff --git a/sca-cpp/trunk/components/sqldb/pgsql-test.cpp b/sca-cpp/trunk/components/sqldb/pgsql-test.cpp
index 7fb6b0c814..1019667285 100644
--- a/sca-cpp/trunk/components/sqldb/pgsql-test.cpp
+++ b/sca-cpp/trunk/components/sqldb/pgsql-test.cpp
@@ -33,7 +33,7 @@ namespace tuscany {
namespace pgsql {
bool testPGSql() {
- PGSql pg("dbname=db", "test");
+ PGSql pg("host=localhost port=5432 dbname=db", "test");
const value k = mklist<value>("a");
assert(hasContent(post(k, string("AAA"), pg)));
@@ -59,7 +59,7 @@ struct getLoop {
bool testGetPerf() {
const value k = mklist<value>("c");
- PGSql pg("dbname=db", "test");
+ PGSql pg("host=localhost port=5432 dbname=db", "test");
assert(hasContent(post(k, string("CCC"), pg)));
const lambda<bool()> gl = getLoop(k, pg);
diff --git a/sca-cpp/trunk/components/sqldb/pgsql.hpp b/sca-cpp/trunk/components/sqldb/pgsql.hpp
index bb37d125b8..f4da8db220 100644
--- a/sca-cpp/trunk/components/sqldb/pgsql.hpp
+++ b/sca-cpp/trunk/components/sqldb/pgsql.hpp
@@ -40,10 +40,13 @@ namespace pgsql {
/**
* Return and clear a Postgres result failure.
*/
-const string pgfailure(PGresult* r) {
- const string e = PQresultErrorMessage(r);
+const string pgfailure(PGresult* r, PGconn* conn) {
+ const string re = PQresultErrorMessage(r);
PQclear(r);
- return e;
+ if (length(re) != 0)
+ return re;
+ const string ce = PQerrorMessage(conn);
+ return ce;
}
/**
@@ -54,19 +57,23 @@ public:
PGSql() : owner(false) {
}
- PGSql(const string& conninfo, const string& table) : owner(true), conninfo(conninfo), table(table) {
- init();
+ PGSql(const string& conninfo, const string& table) : owner(true), conn(NULL), conninfo(conninfo), table(table) {
+ conn = PQconnectdb(c_str(conninfo));
+ if (PQstatus(conn) != CONNECTION_OK) {
+ mkfailure<bool>(string("Could not connect to postgresql database: ") + PQerrorMessage(conn));
+ return;
+ }
+ setup(true);
}
- PGSql(const PGSql& c) : owner(false) {
- conninfo = c.conninfo;
- conn = c.conn;
- table = c.table;
+ PGSql(const PGSql& c) : owner(false), conn(c.conn), conninfo(c.conninfo), table(c.table) {
}
~PGSql() {
if (!owner)
return;
+ if (conn == NULL)
+ return;
PQfinish(conn);
}
@@ -76,28 +83,37 @@ private:
string conninfo;
string table;
+ friend const failable<bool> setup(const PGSql& pgsql);
friend const failable<bool> post(const value& key, const value& val, const PGSql& pgsql);
friend const failable<bool> put(const value& key, const value& val, const PGSql& pgsql);
friend const failable<value> get(const value& key, const PGSql& pgsql);
friend const failable<bool> del(const value& key, const PGSql& pgsql);
/**
- * Initialize the database connection
+ * Setup the database connection.
*/
- const failable<bool> init() {
- conn = PQconnectdb(c_str(conninfo));
- if (PQstatus(conn) != CONNECTION_OK)
- return mkfailure<bool>(string("Could not connect to database: ") + PQerrorMessage(conn));
+ const failable<bool> setup(const bool init) const {
+
+ // Check the status of the connection and reconnect if necessary
+ if (!init) {
+ if (PQstatus(conn) == CONNECTION_OK)
+ return true;
+ debug("pgsql::setup::reset");
+ PQreset(conn);
+ if (PQstatus(conn) != CONNECTION_OK)
+ return mkfailure<bool>(string("Could not reconnect to postgresql database: ") + PQerrorMessage(conn));
+ }
+ debug("pgsql::setup::init");
// Find the name of the first column in the target table
// Assume that's the key we need to use
string ks = string("select a.attname from pg_attribute a, pg_class c where a.attrelid = c.relfilenode and c.relname = '") + table + string("' and a.attnum in (1, 2) order by a.attnum;");
PGresult* kr = PQexec(conn, c_str(ks));
if (PQresultStatus(kr) != PGRES_TUPLES_OK)
- return mkfailure<bool>(string("Could not execute column select statement: ") + pgfailure(kr));
+ return mkfailure<bool>(string("Could not execute postgresql column select statement: ") + pgfailure(kr, conn));
if (PQntuples(kr) != 2) {
PQclear(kr);
- return mkfailure<bool>(string("Could not find table key and value column names"));
+ return mkfailure<bool>(string("Could not find postgresql table key and value column names"));
}
const string kname = PQgetvalue(kr, 0, 0);
const string vname = PQgetvalue(kr, 1, 0);
@@ -107,25 +123,25 @@ private:
{
PGresult* r = PQprepare(conn, "post", c_str(string("insert into ") + table + string(" values($1, $2);")), 2, NULL);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not prepare post SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not prepare post postgresql SQL statement: ") + pgfailure(r, conn));
PQclear(r);
}
{
PGresult* r = PQprepare(conn, "put", c_str(string("update ") + table + string(" set ") + vname + string(" = $2 where ") + kname + string(" = $1;")), 2, NULL);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not prepare put SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not prepare put postgresql SQL statement: ") + pgfailure(r, conn));
PQclear(r);
}
{
PGresult* r = PQprepare(conn, "get", c_str(string("select * from ") + table + string(" where ") + kname + string(" = $1;")), 1, NULL);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not prepare get SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not prepare get postgresql SQL statement: ") + pgfailure(r, conn));
PQclear(r);
}
{
PGresult* r = PQprepare(conn, "delete", c_str(string("delete from ") + table + string(" where ") + kname + string(" = $1;")), 1, NULL);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not prepare delete SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not prepare delete postgresql SQL statement: ") + pgfailure(r, conn));
PQclear(r);
}
return true;
@@ -133,6 +149,13 @@ private:
};
/**
+ * Setup the database connection if necessary.
+ */
+const failable<bool> setup(const PGSql& pgsql) {
+ return pgsql.setup(false);
+}
+
+/**
* Post a new item to the database.
*/
const failable<bool> post(const value& key, const value& val, const PGSql& pgsql) {
@@ -140,13 +163,14 @@ const failable<bool> post(const value& key, const value& val, const PGSql& pgsql
debug(val, "pgsql::post::value");
debug(pgsql.conninfo, "pgsql::post::conninfo");
debug(pgsql.table, "pgsql::post::table");
+ setup(pgsql);
const string ks(scheme::writeValue(key));
const string vs(scheme::writeValue(val));
const char* params[2] = { c_str(ks), c_str(vs) };
PGresult* r = PQexecPrepared(pgsql.conn, "post", 2, params, NULL, NULL, 0);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not execute post SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not execute insert postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
PQclear(r);
debug(true, "pgsql::post::result");
@@ -161,13 +185,14 @@ const failable<bool> put(const value& key, const value& val, const PGSql& pgsql)
debug(val, "pgsql::put::value");
debug(pgsql.conninfo, "pgsql::put::conninfo");
debug(pgsql.table, "pgsql::put::table");
+ setup(pgsql);
const string ks(scheme::writeValue(key));
const string vs(scheme::writeValue(val));
const char* params[2] = { c_str(ks), c_str(vs) };
PGresult* r = PQexecPrepared(pgsql.conn, "put", 2, params, NULL, NULL, 0);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not execute put SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not execute update postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
const string t = PQcmdTuples(r);
if (t != "0") {
PQclear(r);
@@ -178,7 +203,7 @@ const failable<bool> put(const value& key, const value& val, const PGSql& pgsql)
PGresult* pr = PQexecPrepared(pgsql.conn, "post", 2, params, NULL, NULL, 0);
if (PQresultStatus(pr) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not execute post SQL statement: ") + pgfailure(pr));
+ return mkfailure<bool>(string("Could not execute insert postgresql SQL statement: ") + pgfailure(pr, pgsql.conn));
PQclear(pr);
debug(true, "pgsql::put::result");
@@ -192,15 +217,16 @@ const failable<value> get(const value& key, const PGSql& pgsql) {
debug(key, "pgsql::get::key");
debug(pgsql.conninfo, "pgsql::get::conninfo");
debug(pgsql.table, "pgsql::get::table");
+ setup(pgsql);
const string ks(scheme::writeValue(key));
const char* params[1] = { c_str(ks) };
PGresult* r = PQexecPrepared(pgsql.conn, "get", 1, params, NULL, NULL, 0);
if (PQresultStatus(r) != PGRES_TUPLES_OK)
- return mkfailure<value>(string("Could not execute get SQL statement: ") + pgfailure(r));
+ return mkfailure<value>(string("Could not execute select postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
if (PQntuples(r) < 1) {
PQclear(r);
- return mkfailure<value>(string("Could not get entry: ") + PQerrorMessage(pgsql.conn));
+ return mkfailure<value>(string("Could not get postgresql entry: ") + PQerrorMessage(pgsql.conn));
}
const char* data = PQgetvalue(r, 0, 1);
const value val(scheme::readValue(string(data)));
@@ -217,12 +243,13 @@ const failable<bool> del(const value& key, const PGSql& pgsql) {
debug(key, "pgsql::delete::key");
debug(pgsql.conninfo, "pgsql::delete::conninfo");
debug(pgsql.table, "pgsql::delete::table");
+ setup(pgsql);
const string ks(scheme::writeValue(key));
const char* params[1] = { c_str(ks) };
PGresult* r = PQexecPrepared(pgsql.conn, "delete", 1, params, NULL, NULL, 0);
if (PQresultStatus(r) != PGRES_COMMAND_OK)
- return mkfailure<bool>(string("Could not execute delete SQL statement: ") + pgfailure(r));
+ return mkfailure<bool>(string("Could not execute delete postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
PQclear(r);
debug(true, "pgsql::delete::result");
diff --git a/sca-cpp/trunk/components/sqldb/server-test b/sca-cpp/trunk/components/sqldb/server-test
index 784c7156c5..c07d3b0510 100755
--- a/sca-cpp/trunk/components/sqldb/server-test
+++ b/sca-cpp/trunk/components/sqldb/server-test
@@ -19,6 +19,11 @@
# Setup
../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+./pgsql-conf tmp
+./pgsql-start tmp
+./pgsql "drop table test;" 1>/dev/null 2>&1
+./pgsql "create table test(key text, value text);" 1>/dev/null 2>&1
+
../../modules/server/server-conf tmp
../../modules/server/scheme-conf tmp
cat >>tmp/conf/httpd.conf <<EOF
@@ -26,9 +31,6 @@ SCAContribution `pwd`/
SCAComposite sqldb.composite
EOF
-./pgsql-start tmp
-./pgsql "drop table test;" 1>/dev/null 2>&1
-./pgsql "create table test(key text, value text);" 1>/dev/null 2>&1
../../modules/http/httpd-start tmp
sleep 2
@@ -39,5 +41,4 @@ rc=$?
# Cleanup
../../modules/http/httpd-stop tmp
./pgsql-stop tmp
-sleep 2
return $rc
diff --git a/sca-cpp/trunk/components/sqldb/sqldb-test b/sca-cpp/trunk/components/sqldb/sqldb-test
index 05fe413d50..e910ae0059 100755
--- a/sca-cpp/trunk/components/sqldb/sqldb-test
+++ b/sca-cpp/trunk/components/sqldb/sqldb-test
@@ -18,6 +18,7 @@
# under the License.
# Setup
+./pgsql-conf tmp
./pgsql-start tmp
./pgsql "drop table test;" 1>/dev/null 2>&1
./pgsql "create table test(key text, value text);" 1>/dev/null 2>&1
diff --git a/sca-cpp/trunk/components/sqldb/sqldb.composite b/sca-cpp/trunk/components/sqldb/sqldb.composite
index a27332e649..1511e66024 100644
--- a/sca-cpp/trunk/components/sqldb/sqldb.composite
+++ b/sca-cpp/trunk/components/sqldb/sqldb.composite
@@ -24,7 +24,7 @@
<component name="sqldb">
<implementation.cpp path="." library="libsqldb"/>
- <property name="conninfo">dbname=db</property>
+ <property name="conninfo">host=localhost port=5432 dbname=db</property>
<property name="table">test</property>
<service name="sqldb">
<t:binding.http uri="sqldb"/>
diff --git a/sca-cpp/trunk/components/sqldb/sqldb.cpp b/sca-cpp/trunk/components/sqldb/sqldb.cpp
index e84a732511..0524b00bd2 100644
--- a/sca-cpp/trunk/components/sqldb/sqldb.cpp
+++ b/sca-cpp/trunk/components/sqldb/sqldb.cpp
@@ -23,10 +23,7 @@
* PostgreSQL-based database component implementation.
*/
-#include <apr_uuid.h>
-
#include "string.hpp"
-
#include "function.hpp"
#include "list.hpp"
#include "value.hpp"
@@ -46,16 +43,8 @@ const failable<value> get(const list<value>& params, pgsql::PGSql& pg) {
/**
* Post an item to the database.
*/
-const value uuidValue() {
- apr_uuid_t uuid;
- apr_uuid_get(&uuid);
- char buf[APR_UUID_FORMATTED_LENGTH];
- apr_uuid_format(buf, &uuid);
- return value(string(buf, APR_UUID_FORMATTED_LENGTH));
-}
-
const failable<value> post(const list<value>& params, pgsql::PGSql& pg) {
- const value id = append<value>(car(params), mklist(uuidValue()));
+ const value id = append<value>(car(params), mklist(mkuuid()));
const failable<bool> val = pgsql::post(id, cadr(params), pg);
if (!hasContent(val))
return mkfailure<value>(reason(val));
diff --git a/sca-cpp/trunk/components/sqldb/standby-test b/sca-cpp/trunk/components/sqldb/standby-test
new file mode 100755
index 0000000000..e572de6fe2
--- /dev/null
+++ b/sca-cpp/trunk/components/sqldb/standby-test
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+../../modules/http/httpd-conf tmp/master localhost 8090 tmp/master/htdocs
+./pgsql-conf tmp/master 5432
+./pgsql-start tmp/master
+./pgsql localhost 5432 "drop table test;" 1>/dev/null 2>&1
+./pgsql localhost 5432 "create table test(key text, value text);" 1>/dev/null 2>&1
+../../modules/http/httpd-start tmp/master
+sleep 2
+./pgsql-standby-conf tmp/standby localhost 5432 8090 5433
+./pgsql-start tmp/standby
+
+# Test
+./pgsql-standby-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp/master
+./pgsql-stop tmp/standby
+./pgsql-stop tmp/master
+return $rc