PageRenderTime 24ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/util/server.c

http://github.com/mongodb/mongo-php-driver
C | 493 lines | 346 code | 115 blank | 32 comment | 81 complexity | 439adb373449c7d49a4fde812ca099f6 MD5 | raw file
Possible License(s): Apache-2.0
  1. // server.c
  2. /**
  3. * Copyright 2009-2011 10gen, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. #include <php.h>
  18. #include "../php_mongo.h"
  19. #include "../bson.h"
  20. #include "../mongo.h"
  21. #include "../db.h"
  22. #include "rs.h"
  23. #include "server.h"
  24. #include "log.h"
  25. #include "pool.h"
  26. extern int le_pserver;
  27. extern zend_class_entry *mongo_ce_Id;
  28. static server_info* create_info();
  29. static void make_other_le(const char *id, server_info *info TSRMLS_DC);
  30. static server_info* wrap_other_guts(server_info *source);
  31. static char* get_server_id(char *host);
  32. static void mongo_util_server__down(server_info *server);
  33. // we only want to call this every INTERVAL seconds
  34. static int mongo_util_server_reconnect(mongo_server *server TSRMLS_DC);
  35. mongo_server* mongo_util_server_copy(const mongo_server *source, mongo_server *dest, int persist TSRMLS_DC) {
  36. // we assume if def was persistent it will still be persistent and visa versa
  37. if (dest) {
  38. php_mongo_server_free(dest, persist TSRMLS_CC);
  39. }
  40. dest = (mongo_server*)pemalloc(sizeof(mongo_server), persist);
  41. memset(dest, 0, sizeof(mongo_server));
  42. dest->host = pestrdup(source->host, persist);
  43. dest->port = source->port;
  44. dest->label = pestrdup(source->label, persist);
  45. if (source->username && source->password && source->db) {
  46. dest->username = pestrdup(source->username, persist);
  47. dest->password = pestrdup(source->password, persist);
  48. dest->db = pestrdup(source->db, persist);
  49. }
  50. mongo_util_pool_get(dest, 0 TSRMLS_CC);
  51. return dest;
  52. }
  53. int mongo_util_server_cmp(char *host1, char *host2 TSRMLS_DC) {
  54. char *id1 = 0, *id2 = 0;
  55. int result = 0;
  56. zend_rsrc_list_entry *le1 = 0, *le2 = 0;
  57. id1 = get_server_id(host1);
  58. id2 = get_server_id(host2);
  59. if (zend_hash_find(&EG(persistent_list), id1, strlen(id1)+1, (void**)&le1) == SUCCESS &&
  60. zend_hash_find(&EG(persistent_list), id2, strlen(id2)+1, (void**)&le2) == SUCCESS &&
  61. ((server_info*)le1->ptr)->guts == ((server_info*)le2->ptr)->guts) {
  62. mongo_log(MONGO_LOG_SERVER, MONGO_LOG_INFO TSRMLS_CC, "server: detected that %s is the same server as %s",
  63. host1, host2);
  64. // result is initialized to 0
  65. }
  66. else {
  67. result = strcmp(id1, id2);
  68. }
  69. efree(id1);
  70. efree(id2);
  71. return result;
  72. }
  73. static int mongo_util_server_reconnect(mongo_server *server TSRMLS_DC) {
  74. // if the server is down, try to reconnect
  75. if (!server->connected &&
  76. mongo_util_pool_refresh(server, MONGO_RS_TIMEOUT TSRMLS_CC) == FAILURE) {
  77. return FAILURE;
  78. }
  79. return SUCCESS;
  80. }
  81. int mongo_util_server_ping(mongo_server *server, time_t now TSRMLS_DC) {
  82. server_info* info;
  83. zval *response = 0, **ok = 0;
  84. struct timeval start, end;
  85. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  86. return FAILURE;
  87. }
  88. // call ismaster every ISMASTER_INTERVAL seconds
  89. if (info->guts->last_ismaster + MONGO_ISMASTER_INTERVAL <= now) {
  90. if (mongo_util_server_reconnect(server TSRMLS_CC) == FAILURE) {
  91. return FAILURE;
  92. }
  93. return mongo_util_server_ismaster(info, server, now TSRMLS_CC);
  94. }
  95. if (info->guts->last_ping + MONGO_PING_INTERVAL > now) {
  96. return info->guts->readable ? SUCCESS : FAILURE;
  97. }
  98. if (mongo_util_server_reconnect(server TSRMLS_CC) == FAILURE) {
  99. return FAILURE;
  100. }
  101. gettimeofday(&start, 0);
  102. response = mongo_util_rs__cmd("ping", server TSRMLS_CC);
  103. gettimeofday(&end, 0);
  104. mongo_util_server__set_ping(info, start, end);
  105. if (!response) {
  106. mongo_util_server__down(info);
  107. return FAILURE;
  108. }
  109. // TODO: clear exception?
  110. zend_hash_find(HASH_P(response), "ok", strlen("ok")+1, (void**)&ok);
  111. if (Z_NUMVAL_PP(ok, 1)) {
  112. mongo_util_server_ismaster(info, server, now TSRMLS_CC);
  113. }
  114. zval_ptr_dtor(&response);
  115. return info->guts->readable ? SUCCESS : FAILURE;
  116. }
  117. int mongo_util_server_ismaster(server_info *info, mongo_server *server, time_t now TSRMLS_DC) {
  118. zval *response = 0, **secondary = 0, **bson = 0, **self = 0;
  119. response = mongo_util_rs__cmd("ismaster", server TSRMLS_CC);
  120. // update last_ismaster
  121. info->guts->last_ismaster = now;
  122. if (!response) {
  123. return FAILURE;
  124. }
  125. zend_hash_find(HASH_P(response), "me", strlen("me")+1, (void**)&self);
  126. if (!info->guts->pinged && self &&
  127. strncmp(Z_STRVAL_PP(self), server->label, Z_STRLEN_PP(self)) != 0) {
  128. // this server thinks its name is different than what we have recorded
  129. mongo_log(MONGO_LOG_SERVER, MONGO_LOG_INFO TSRMLS_CC, "server: found another name for %s: %s",
  130. server->label, Z_STRVAL_PP(self));
  131. // make a new info entry for this name, pointing to our info
  132. make_other_le(Z_STRVAL_PP(self), info TSRMLS_CC);
  133. }
  134. // now we have pinged it at least once
  135. info->guts->pinged = 1;
  136. zend_hash_find(HASH_P(response), "secondary", strlen("secondary")+1, (void**)&secondary);
  137. if (secondary && Z_BVAL_PP(secondary)) {
  138. if (!info->guts->readable) {
  139. mongo_log(MONGO_LOG_SERVER, MONGO_LOG_INFO TSRMLS_CC, "server: %s is now a secondary", server->label);
  140. }
  141. info->guts->readable = 1;
  142. info->guts->master = 0;
  143. }
  144. else if (mongo_util_rs__get_ismaster(response TSRMLS_CC)) {
  145. if (!info->guts->master) {
  146. mongo_log(MONGO_LOG_SERVER, MONGO_LOG_INFO TSRMLS_CC, "server: %s is now primary", server->label);
  147. }
  148. info->guts->master = 1;
  149. info->guts->readable = 1;
  150. }
  151. else {
  152. if (info->guts->readable) {
  153. mongo_log(MONGO_LOG_SERVER, MONGO_LOG_INFO TSRMLS_CC, "server: %s is now not readable", server->label);
  154. }
  155. mongo_util_server__down(info);
  156. }
  157. zend_hash_find(HASH_P(response), "maxBsonObjectSize", strlen("maxBsonObjectSize")+1, (void**)&bson);
  158. if (bson) {
  159. if (Z_TYPE_PP(bson) == IS_LONG) {
  160. info->guts->max_bson_size = Z_LVAL_PP(bson);
  161. }
  162. else if (Z_TYPE_PP(bson) == IS_DOUBLE) {
  163. info->guts->max_bson_size = (int)Z_DVAL_PP(bson);
  164. }
  165. // otherwise, leave as the default
  166. else {
  167. mongo_log(MONGO_LOG_SERVER, MONGO_LOG_WARNING TSRMLS_CC,
  168. "server: could not find max bson size on %s, consider upgrading your server", server->label);
  169. }
  170. }
  171. zval_ptr_dtor(&response);
  172. return SUCCESS;
  173. }
  174. void mongo_util_server__prime(server_info *info, mongo_server *server TSRMLS_DC) {
  175. if (info->guts->pinged) {
  176. return;
  177. }
  178. mongo_util_server_ping(server, MONGO_SERVER_PING TSRMLS_CC);
  179. }
  180. int mongo_util_server_get_bucket(mongo_server *server TSRMLS_DC) {
  181. server_info* info;
  182. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  183. return FAILURE;
  184. }
  185. mongo_util_server__prime(info, server TSRMLS_CC);
  186. return info->guts->bucket;
  187. }
  188. int mongo_util_server_get_state(mongo_server *server TSRMLS_DC) {
  189. server_info* info;
  190. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  191. return 0;
  192. }
  193. mongo_util_server__prime(info, server TSRMLS_CC);
  194. if (info->guts->master) {
  195. return 1;
  196. }
  197. else if (info->guts->readable) {
  198. return 2;
  199. }
  200. else {
  201. return 0;
  202. }
  203. }
  204. int mongo_util_server__set_ping(server_info *info, struct timeval start, struct timeval end) {
  205. info->guts->last_ping = start.tv_sec;
  206. // in microsecs
  207. info->guts->ping = (end.tv_sec - start.tv_sec)*1000+(end.tv_usec - start.tv_usec)/1000;
  208. // clocks might return weird stuff
  209. if (info->guts->ping < 0) {
  210. info->guts->ping = 0;
  211. }
  212. // buckets for 0, 16, 256, etc.
  213. if (!info->guts->ping) {
  214. info->guts->bucket = 0;
  215. }
  216. else {
  217. int temp_ping = info->guts->ping;
  218. info->guts->bucket = 0;
  219. while (temp_ping) {
  220. temp_ping /= 16;
  221. info->guts->bucket++;
  222. }
  223. }
  224. return info->guts->ping;
  225. }
  226. int mongo_util_server_get_bson_size(mongo_server *server TSRMLS_DC) {
  227. server_info* info;
  228. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  229. return MONGO_SERVER_BSON;
  230. }
  231. mongo_util_server__prime(info, server TSRMLS_CC);
  232. return info->guts->max_bson_size;
  233. }
  234. int mongo_util_server_set_readable(mongo_server *server, zend_bool readable TSRMLS_DC) {
  235. server_info* info;
  236. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  237. return FAILURE;
  238. }
  239. info->guts->readable = readable;
  240. return SUCCESS;
  241. }
  242. int mongo_util_server_get_readable(mongo_server *server TSRMLS_DC) {
  243. server_info* info;
  244. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  245. return 0;
  246. }
  247. mongo_util_server__prime(info, server TSRMLS_CC);
  248. return info->guts->readable;
  249. }
  250. void mongo_util_server_down(mongo_server* server TSRMLS_DC) {
  251. server_info* info;
  252. if ((info = mongo_util_server__get_info(server TSRMLS_CC)) == 0) {
  253. return;
  254. }
  255. mongo_util_server__down(info);
  256. }
  257. void mongo_util_server__down(server_info *info) {
  258. info->guts->readable = 0;
  259. info->guts->master = 0;
  260. }
  261. static char* get_server_id(char *host) {
  262. char *id;
  263. id = (char*)emalloc(strlen(host)+strlen(MONGO_SERVER_INFO)+2);
  264. mongo_buf_init(id);
  265. mongo_buf_append(id, MONGO_SERVER_INFO);
  266. mongo_buf_append(id, ":");
  267. mongo_buf_append(id, host);
  268. return id;
  269. }
  270. server_info* mongo_util_server__get_info(mongo_server *server TSRMLS_DC) {
  271. zend_rsrc_list_entry *le = 0;
  272. char *id = get_server_id(server->label);
  273. if (zend_hash_find(&EG(persistent_list), id, strlen(id)+1, (void**)&le) == FAILURE) {
  274. zend_rsrc_list_entry nle;
  275. server_info *info = 0;
  276. // clear to start so that we can check it was set later
  277. nle.ptr = 0;
  278. info = create_info();
  279. if (info != 0) {
  280. info->owner = 1;
  281. nle.ptr = info;
  282. nle.refcount = 1;
  283. nle.type = le_pserver;
  284. }
  285. if (nle.ptr) {
  286. zend_hash_add(&EG(persistent_list), id, strlen(id)+1, &nle, sizeof(zend_rsrc_list_entry), NULL);
  287. }
  288. efree(id);
  289. return info;
  290. }
  291. efree(id);
  292. return le->ptr;
  293. }
  294. static server_info* wrap_other_guts(server_info *source) {
  295. server_info *info;
  296. info = (server_info*)pemalloc(sizeof(server_info), 1);
  297. info->owner = 0;
  298. info->guts = source->guts;
  299. return info;
  300. }
  301. static server_info* create_info() {
  302. server_info *info;
  303. server_guts *guts;
  304. info = (server_info*)pemalloc(sizeof(server_info), 1);
  305. guts = (server_guts*)pemalloc(sizeof(server_guts), 1);
  306. memset(guts, 0, sizeof(server_guts));
  307. guts->ping = MONGO_SERVER_PING;
  308. guts->max_bson_size = MONGO_SERVER_BSON;
  309. memset(info, 0, sizeof(server_info));
  310. info->guts = guts;
  311. return info;
  312. }
  313. static void make_other_le(const char *id, server_info *info TSRMLS_DC) {
  314. zend_rsrc_list_entry *le = 0, nle;
  315. if (zend_hash_find(&EG(persistent_list), id, strlen(id)+1, (void**)&le) == SUCCESS) {
  316. return;
  317. }
  318. nle.ptr = wrap_other_guts(info);
  319. nle.type = le_pserver;
  320. nle.refcount = 1;
  321. zend_hash_add(&EG(persistent_list), id, strlen(id)+1, &nle, sizeof(zend_rsrc_list_entry), NULL);
  322. }
  323. #ifdef WIN32
  324. void gettimeofday(struct timeval *t, void* tz) {
  325. SYSTEMTIME ft;
  326. if (t != 0) {
  327. GetSystemTime(&ft);
  328. t->tv_sec = ft.wSecond;
  329. t->tv_usec = ft.wMilliseconds*1000;
  330. }
  331. }
  332. #endif
  333. void mongo_util_server_shutdown(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
  334. server_info *info;
  335. if (!rsrc || !rsrc->ptr) {
  336. return;
  337. }
  338. info = (server_info*)rsrc->ptr;
  339. if (info->owner) {
  340. pefree(info->guts, 1);
  341. info->guts = 0;
  342. }
  343. pefree(info, 1);
  344. rsrc->ptr = 0;
  345. }
  346. PHP_METHOD(Mongo, serverInfo) {
  347. HashPosition pointer;
  348. zend_rsrc_list_entry *le;
  349. array_init(return_value);
  350. for (zend_hash_internal_pointer_reset_ex(&EG(persistent_list), &pointer);
  351. zend_hash_get_current_data_ex(&EG(persistent_list), (void**) &le, &pointer) == SUCCESS;
  352. zend_hash_move_forward_ex(&EG(persistent_list), &pointer)) {
  353. zval *m;
  354. char *key;
  355. unsigned int key_len;
  356. unsigned long index;
  357. server_info *info;
  358. if (!le || le->type != le_pserver) {
  359. continue;
  360. }
  361. info = (server_info*)le->ptr;
  362. MAKE_STD_ZVAL(m);
  363. array_init(m);
  364. add_assoc_bool(m, "owner", info->owner);
  365. add_assoc_long(m, "last ping", info->guts->last_ping);
  366. add_assoc_long(m, "ping (ms)", info->guts->ping);
  367. add_assoc_long(m, "master", info->guts->master);
  368. add_assoc_long(m, "readable", info->guts->readable);
  369. add_assoc_long(m, "max BSON size", info->guts->max_bson_size);
  370. if (zend_hash_get_current_key_ex(&EG(persistent_list), &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) {
  371. add_assoc_zval(return_value, key, m);
  372. }
  373. else {
  374. add_index_zval(return_value, index, m);
  375. }
  376. }
  377. // return_value is returned
  378. }