|
1 | | -/** |
2 | | - * @file |
3 | | - * The CBMAggregatorHybridLibPQXXWriter module writes the stand-level information gathered |
4 | | - * by CBMAggregatorLandUnitData into a PostgreSQL database. It is designed mainly for |
5 | | - * distributed runs where the simulation is divided up and each portion of work is loaded |
6 | | - * into a separate set of tables before being merged together with a post-processing script, |
7 | | - * although this module can also be used for a standard simulation |
8 | | - ********/ |
9 | | - |
10 | 1 | #include "moja/modules/cbm/cbmaggregatorhybridlibpqxxwriter.h" |
11 | 2 |
|
12 | 3 | #include <moja/flint/recordaccumulatorwithmutex.h> |
|
22 | 13 | #include <boost/algorithm/string.hpp> |
23 | 14 | #include <boost/algorithm/string/replace.hpp> |
24 | 15 | #include <boost/format.hpp> |
| 16 | +#include <boost/interprocess/sync/scoped_lock.hpp> |
| 17 | +#include <boost/interprocess/sync/file_lock.hpp> |
25 | 18 |
|
26 | 19 | using namespace pqxx; |
27 | 20 | using Poco::format; |
@@ -63,72 +56,48 @@ namespace cbm { |
63 | 56 | MOJA_LOG_INFO << (boost::format("Loading results into %1% on server: %2%") |
64 | 57 | % _schema % _chConnectionString).str(); |
65 | 58 |
|
66 | | - connection chConn(_chConnectionString); |
67 | | - |
68 | | - std::string guardTable = (boost::format("completed_%1%") % _jobId).str(); |
69 | | - bool resultsPreviouslyLoaded = perform([&chConn, &guardTable, this] { |
70 | | - work tx(chConn); |
71 | | - row result = tx.exec1((boost::format( |
72 | | - "EXISTS TABLE %1%.%2%;" |
73 | | - ) % _schema % guardTable).str()); |
74 | | - |
75 | | - tx.commit(); |
76 | | - return result[0].as<int>() == 1; |
77 | | - }); |
| 59 | + connection conn(_chConnectionString); |
78 | 60 |
|
79 | | - if (resultsPreviouslyLoaded) { |
| 61 | + if (checkCompleted(conn)) { |
80 | 62 | MOJA_LOG_INFO << "Results previously loaded for jobId " << _jobId << " - skipping."; |
81 | 63 | return; |
82 | 64 | } |
83 | 65 |
|
84 | | - perform([&chConn, &guardTable, this] { |
85 | | - work chTx(chConn); |
| 66 | + { |
| 67 | + std::string lockName = (boost::format("%1%.lock") % _jobId).str(); |
| 68 | + boost::interprocess::scoped_lock lock{ |
| 69 | + boost::interprocess::file_lock{lockName.c_str()} |
| 70 | + }; |
86 | 71 |
|
87 | | - // ClickHouse doesn't support unique constraints, so the guard against |
88 | | - // duplicate loads for the same job has to be a table. If this is a |
89 | | - // duplicate, the transaction will fail and roll back the data load. |
90 | | - chTx.exec((boost::format("CREATE VIEW %1%.%2% AS SELECT 1;") % _schema % guardTable).str()); |
91 | | - |
92 | | - load(chTx, (boost::format("%1%.raw_fluxes") % _schema).str(), _fluxDimension); |
93 | | - load(chTx, (boost::format("%1%.raw_pools") % _schema).str(), _poolDimension); |
94 | | - load(chTx, (boost::format("%1%.raw_errors") % _schema).str(), _errorDimension); |
95 | | - load(chTx, (boost::format("%1%.raw_ages") % _schema).str(), _ageDimension); |
96 | | - load(chTx, (boost::format("%1%.raw_disturbances") % _schema).str(), _disturbanceDimension); |
97 | | - |
98 | | - chTx.commit(); |
99 | | - }); |
100 | | - |
101 | | - MOJA_LOG_INFO << "PostgreSQL insert complete." << std::endl; |
102 | | - } |
| 72 | + perform([&conn, this] { |
| 73 | + if (checkCompleted(conn)) { |
| 74 | + MOJA_LOG_INFO << "Results previously loaded for jobId " << _jobId << " - skipping."; |
| 75 | + return; |
| 76 | + } |
103 | 77 |
|
104 | | - void CBMAggregatorHybridLibPQXXWriter::doIsolated(pqxx::connection_base& conn, std::string sql, bool optional) { |
105 | | - perform([&conn, sql, optional] { |
106 | | - try { |
107 | 78 | work tx(conn); |
108 | | - tx.exec(sql); |
| 79 | + tx.exec((boost::format("INSERT INTO %1%.completed_jobs VALUES (%2%);") % _schema % _jobId).str()); |
| 80 | + load(tx, (boost::format("%1%.raw_fluxes") % _schema).str(), _fluxDimension); |
| 81 | + load(tx, (boost::format("%1%.raw_pools") % _schema).str(), _poolDimension); |
| 82 | + load(tx, (boost::format("%1%.raw_errors") % _schema).str(), _errorDimension); |
| 83 | + load(tx, (boost::format("%1%.raw_ages") % _schema).str(), _ageDimension); |
| 84 | + load(tx, (boost::format("%1%.raw_disturbances") % _schema).str(), _disturbanceDimension); |
109 | 85 | tx.commit(); |
110 | | - } catch (...) { |
111 | | - if (!optional) { |
112 | | - throw; |
113 | | - } |
114 | | - } |
115 | | - }); |
| 86 | + }); |
| 87 | + } |
| 88 | + |
| 89 | + MOJA_LOG_INFO << "Insert complete." << std::endl; |
116 | 90 | } |
117 | 91 |
|
118 | | - void CBMAggregatorHybridLibPQXXWriter::doIsolated(pqxx::connection_base& conn, std::vector<std::string> sql, bool optional) { |
119 | | - perform([&conn, sql, optional] { |
120 | | - try { |
121 | | - work tx(conn); |
122 | | - for (auto stmt : sql) { |
123 | | - tx.exec(stmt); |
124 | | - } |
| 92 | + bool CBMAggregatorHybridLibPQXXWriter::checkCompleted(pqxx::connection_base& conn) { |
| 93 | + return perform([&conn, this] { |
| 94 | + work tx(conn); |
| 95 | + row result = tx.exec1((boost::format( |
| 96 | + "SELECT EXISTS (SELECT * FROM %1%.completed_jobs WHERE id = %2%);" |
| 97 | + ) % _schema % _jobId).str()); |
125 | 98 |
|
126 | | - tx.commit(); |
127 | | - } catch (...) { |
128 | | - if (!optional) { |
129 | | - throw; |
130 | | - } |
131 | | - } |
| 99 | + tx.commit(); |
| 100 | + return result[0].as<int>() == 1; |
132 | 101 | }); |
133 | 102 | } |
134 | 103 |
|
|
0 commit comments