ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

326 lines
7.7 KiB

  1. #include "session_sid.h"
  2. #include "global_config.h"
  3. #include "session_sqlite_storage.h"
  4. #include "cppcms_error.h"
  5. #include "posix_mutex.h"
  6. #include <sqlite3.h>
  7. #include <pthread.h>
  8. #include <poll.h>
  9. #include "config.h"
  10. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  11. # include <boost/lexical_cast.hpp>
  12. #else // Internal Boost
  13. # include <cppcms_boost/lexical_cast.hpp>
  14. namespace boost = cppcms_boost;
  15. #endif
  16. namespace cppcms {
  17. namespace storage {
  18. class sqlite {
  19. sqlite3 *db;
  20. pthread_mutex_t mutexes;
  21. int write_ops;
  22. time_t last_commit;
  23. int deferred_commit_count;
  24. int deferred_commit_time;
  25. pthread_mutex_t mutex;
  26. public:
  27. void exec(char const *s)
  28. {
  29. char *err=NULL;
  30. int res;
  31. while((res=sqlite3_exec(db,s,NULL,NULL,&err))!=0) {
  32. if(res==SQLITE_BUSY) {
  33. sqlite3_free(err);
  34. poll(NULL,0,5);
  35. continue;
  36. }
  37. string err_msg=err;
  38. sqlite3_free(err);
  39. throw cppcms_error(err_msg);
  40. }
  41. }
  42. sqlite (string db_name,
  43. bool sync_,
  44. int deferred_commit_count_,
  45. int deferred_commit_time_) :
  46. db(0),
  47. deferred_commit_count(deferred_commit_count_),
  48. deferred_commit_time (deferred_commit_time_)
  49. {
  50. pthread_mutex_init(&mutex,NULL);
  51. try {
  52. if(sqlite3_open(db_name.c_str(),&db)) {
  53. string error=sqlite3_errmsg(db);
  54. throw cppcms_error(error);
  55. }
  56. exec( "CREATE TABLE IF NOT EXISTS sessions ( "
  57. " sid varchar(32) primary key not null, "
  58. " data varchar(1) not null, "
  59. " timeout integer not null "
  60. ")");
  61. exec( "CREATE INDEX IF NOT EXISTS sessions_timeout "
  62. " ON sessions(timeout)");
  63. if(!sync_) {
  64. exec( "PRAGMA synchronous=OFF");
  65. }
  66. write_ops=0;
  67. last_commit=time(NULL);
  68. exec("begin");
  69. }
  70. catch(...) {
  71. if(db) sqlite3_close(db);
  72. pthread_mutex_destroy(&mutex);
  73. throw;
  74. }
  75. }
  76. ~sqlite()
  77. {
  78. try {
  79. exec("commit");
  80. }
  81. catch(...) {
  82. }
  83. if(db) sqlite3_close(db);
  84. if(deferred_commit_count > 0)
  85. pthread_mutex_destroy(&mutex);
  86. }
  87. void check_commits()
  88. {
  89. long long int now=time(NULL);
  90. if( write_ops >= deferred_commit_count
  91. || (deferred_commit_time > 0 && last_commit + deferred_commit_time < now))
  92. {
  93. char stat[128];
  94. snprintf(stat,sizeof(stat),"DELETE FROM sessions WHERE timeout < %lld",now);
  95. exec(stat);
  96. exec("commit");
  97. exec("begin");
  98. last_commit=time(NULL);
  99. write_ops=0;
  100. }
  101. }
  102. void exec(char const *s,string const &sid,string const &data,int64_t timeout)
  103. {
  104. sqlite3_stmt *st;
  105. if(sqlite3_prepare(db,s,-1,&st,NULL)!=0) {
  106. throw cppcms_error(string("sqlite prepared statement:")+sqlite3_errmsg(db));
  107. }
  108. sqlite3_bind_text(st,1, sid.c_str(), sid.size(),SQLITE_STATIC);
  109. sqlite3_bind_blob(st,2,data.c_str(),data.size(),SQLITE_STATIC);
  110. sqlite3_bind_int64(st,3,timeout);
  111. int res;
  112. while((res=sqlite3_step(st))==SQLITE_BUSY){
  113. poll(NULL,0,5);
  114. }
  115. if(res==SQLITE_DONE) {
  116. sqlite3_finalize(st);
  117. }
  118. else {
  119. throw cppcms_error(string("Insert error:")+sqlite3_errmsg(db));
  120. }
  121. }
  122. bool select(string const &sid,time_t &timeout,string &data)
  123. {
  124. sqlite3_stmt *st;
  125. char const *q="SELECT data,timeout FROM sessions WHERE sid=?";
  126. if(sqlite3_prepare(db,q,-1,&st,NULL)!=0) {
  127. throw cppcms_error(string("sqlite prepared statement:")+sqlite3_errmsg(db));
  128. }
  129. int res;
  130. sqlite3_bind_text(st,1, sid.c_str(), sid.size(),SQLITE_STATIC);
  131. while((res=sqlite3_step(st))==SQLITE_BUSY){
  132. poll(NULL,0,5);
  133. }
  134. if(res==SQLITE_DONE) {
  135. sqlite3_finalize(st);
  136. return false;
  137. }
  138. else if(res==SQLITE_ROW) {
  139. int64_t to=sqlite3_column_int64(st,1);
  140. if(to < time(NULL)) {
  141. sqlite3_finalize(st);
  142. del(sid);
  143. write_ops++;
  144. return false;
  145. }
  146. size_t length=sqlite3_column_bytes(st,0);
  147. data.assign((const char *)sqlite3_column_blob(st,0),length);
  148. timeout=to;
  149. sqlite3_finalize(st);
  150. return true;
  151. }
  152. else {
  153. throw cppcms_error(string("Insert error:")+sqlite3_errmsg(db));
  154. }
  155. }
  156. void save(string const &sid,time_t timeout,string const &data)
  157. {
  158. mutex_lock lock(mutex);
  159. exec("INSERT OR REPLACE INTO sessions VALUES(?,?,?)",sid,data,timeout);
  160. write_ops++;
  161. check_commits();
  162. }
  163. void del(string const &sid)
  164. {
  165. char q[128];
  166. snprintf(q,sizeof(q),"DELETE FROM sessions WHERE sid='%s'",sid.c_str());
  167. exec(q);
  168. }
  169. void remove(string const &sid)
  170. {
  171. mutex_lock lock(mutex);
  172. del(sid);
  173. write_ops++;
  174. check_commits();
  175. }
  176. bool load(std::string const &sid,time_t *timeout,std::string &out)
  177. {
  178. mutex_lock lock(mutex);
  179. time_t tout;
  180. if(!select(sid,tout,out)) {
  181. check_commits();
  182. return false;
  183. }
  184. if(timeout) *timeout=tout;
  185. check_commits();
  186. return true;
  187. }
  188. };
  189. sqlite &sqlite_N::db(string const &sid)
  190. {
  191. char buf[3]={ sid.at(10) , sid.at(15) , 0 };
  192. int v;
  193. sscanf(buf,"%x",&v);
  194. v = v%size;
  195. return *dbs.at(v);
  196. }
  197. sqlite_N::sqlite_N(string db,int n,bool sync,int def_comm,int def_to) :
  198. size(n)
  199. {
  200. dbs.resize(n);
  201. int i;
  202. for(i=0;i<n;i++) {
  203. string fname=db+"_"+boost::lexical_cast<string>(i);
  204. dbs[i]=boost::shared_ptr<sqlite>(new sqlite(fname,sync,def_comm,def_to));
  205. }
  206. }
  207. bool sqlite_N::load(std::string const &sid,time_t *timeout,std::string &out)
  208. {
  209. return db(sid).load(sid,timeout,out);
  210. }
  211. void sqlite_N::remove(string const &sid)
  212. {
  213. db(sid).remove(sid);
  214. }
  215. void sqlite_N::save(string const &sid,time_t timeout,string const &data)
  216. {
  217. db(sid).save(sid,timeout,data);
  218. }
  219. } // storage
  220. #ifndef NO_BUILDER_INTERFACE
  221. namespace {
  222. // The database is created at startup
  223. struct builder_thread {
  224. boost::shared_ptr<storage::sqlite_N> db;
  225. bool cache;
  226. builder_thread(string dir,int n,bool sync,int dc,int dt,bool c) :
  227. db(new storage::sqlite_N(dir,n,sync,dc,dt)),
  228. cache(c)
  229. {
  230. }
  231. boost::shared_ptr<session_api> operator()(worker_thread &w)
  232. {
  233. boost::shared_ptr<session_server_storage> storage(new session_sqlite_storage(db));
  234. return boost::shared_ptr<session_api>(new session_sid(storage,cache));
  235. }
  236. };
  237. // The database created *AFTER* forks + no deferred commits
  238. struct builder_proc {
  239. string dir;
  240. bool sync;
  241. int size;
  242. bool cache;
  243. builder_proc(string d,int n,bool s,bool c) : dir(d) , sync(s) , size(n), cache(c)
  244. {
  245. }
  246. boost::shared_ptr<session_api> operator()(worker_thread &w)
  247. {
  248. boost::shared_ptr<storage::sqlite_N> db(new storage::sqlite_N(dir,size,sync,0,0));
  249. boost::shared_ptr<session_server_storage> storage(new session_sqlite_storage(db));
  250. return boost::shared_ptr<session_api>(new session_sid(storage,cache));
  251. }
  252. };
  253. }
  254. session_backend_factory session_sqlite_storage::factory(cppcms_config const &config)
  255. {
  256. string db=config.sval("session.sqlite_db");
  257. int db_count=config.ival("session.sqlite_db_num",4);
  258. if(db_count>8)
  259. db_count=8;
  260. if(db_count<0)
  261. db_count=0;
  262. db_count=1<<db_count;
  263. string def="fork";
  264. if(config.sval("server.mod","")=="thread")
  265. def="thread";
  266. string mod=config.sval("session.sqlite_mod",def);
  267. bool cache=config.ival("session.server_enable_cache",0);
  268. if(mod=="fork") {
  269. bool sync=config.ival("session.sqlite_sync",0);
  270. return builder_proc(db,db_count,sync,cache);
  271. }
  272. else if(mod=="thread") {
  273. bool sync=config.ival("session.sqlite_sync",1);
  274. int dc=config.ival("session.sqlite_commits",1000);
  275. int dt=config.ival("session.sqlite_commit_timeout",5);
  276. return builder_thread(db,db_count,sync,dc,dt,cache);
  277. }
  278. else {
  279. throw cppcms_error("Unknown sqlite mode:"+mod);
  280. }
  281. }
  282. #else // NO_BUILDER_INTERFACE
  283. session_backend_factory session_sqlite_storage::factory(cppcms_config const &config)
  284. {
  285. throw runtime_error("session_sqlite_storage::factory should bot be used");
  286. }
  287. #endif
  288. session_sqlite_storage::session_sqlite_storage(boost::shared_ptr<storage::sqlite_N> db_):
  289. db(db_)
  290. {
  291. }
  292. void session_sqlite_storage::save(std::string const &sid,time_t timeout,std::string const &in)
  293. {
  294. db->save(sid,timeout,in);
  295. }
  296. bool session_sqlite_storage::load(std::string const &sid,time_t *timeout,std::string &out)
  297. {
  298. return db->load(sid,timeout,out);
  299. }
  300. void session_sqlite_storage::remove(std::string const &sid)
  301. {
  302. return db->remove(sid);
  303. }
  304. } // cppcms