retrocl_trim.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * License: GPL (version 3 or any later version).
  7. * See LICENSE for details.
  8. * END COPYRIGHT BLOCK **/
  9. #ifdef HAVE_CONFIG_H
  10. # include <config.h>
  11. #endif
  12. #include "retrocl.h"
  13. typedef struct _trim_status {
  14. time_t ts_c_max_age; /* Constraint - max age of a changelog entry */
  15. time_t ts_s_last_trim; /* Status - last time we trimmed */
  16. int ts_s_initialized; /* Status - non-zero if initialized */
  17. int ts_s_trimming; /* non-zero if trimming in progress */
  18. PRLock *ts_s_trim_mutex; /* protects ts_s_trimming */
  19. } trim_status;
  20. static trim_status ts = {0L, 0L, 0, 0, NULL};
  21. static int trim_interval = DEFAULT_CHANGELOGDB_TRIM_INTERVAL; /* in second */
  22. /*
  23. * All standard changeLogEntry attributes (initialized in get_cleattrs)
  24. */
  25. static const char *cleattrs[ 10 ] = { NULL, NULL, NULL, NULL, NULL, NULL,
  26. NULL, NULL, NULL };
  27. static int retrocl_trimming = 0;
  28. static Slapi_Eq_Context retrocl_trim_ctx = NULL;
  29. /*
  30. * Function: get_cleattrs
  31. *
  32. * Returns: an array of pointers to attribute names.
  33. *
  34. * Arguments: None.
  35. *
  36. * Description: Initializes, if necessary, and returns an array of char *s
  37. * with attribute names used for retrieving changeLogEntry
  38. * entries from the directory.
  39. */
  40. static const char **get_cleattrs(void)
  41. {
  42. if ( cleattrs[ 0 ] == NULL ) {
  43. cleattrs[ 0 ] = attr_objectclass;
  44. cleattrs[ 1 ] = attr_changenumber;
  45. cleattrs[ 2 ] = attr_targetdn;
  46. cleattrs[ 3 ] = attr_changetype;
  47. cleattrs[ 4 ] = attr_newrdn;
  48. cleattrs[ 5 ] = attr_deleteoldrdn;
  49. cleattrs[ 6 ] = attr_changes;
  50. cleattrs[ 7 ] = attr_newsuperior;
  51. cleattrs[ 8 ] = attr_changetime;
  52. cleattrs[ 9 ] = NULL;
  53. }
  54. return cleattrs;
  55. }
  56. /*
  57. * Function: delete_changerecord
  58. *
  59. * Returns: LDAP_ error code
  60. *
  61. * Arguments: the number of the change to delete
  62. *
  63. * Description:
  64. *
  65. */
  66. static int
  67. delete_changerecord( changeNumber cnum )
  68. {
  69. Slapi_PBlock *pb;
  70. char *dnbuf;
  71. int delrc;
  72. dnbuf = slapi_ch_smprintf("%s=%ld, %s", attr_changenumber, cnum,
  73. RETROCL_CHANGELOG_DN);
  74. pb = slapi_pblock_new ();
  75. slapi_delete_internal_set_pb ( pb, dnbuf, NULL /*controls*/, NULL /* uniqueid */,
  76. g_plg_identity[PLUGIN_RETROCL], 0 /* actions */ );
  77. slapi_delete_internal_pb (pb);
  78. slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &delrc );
  79. slapi_pblock_destroy( pb );
  80. if ( delrc != LDAP_SUCCESS ) {
  81. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  82. "delete_changerecord: could not delete change record %lu (rc: %d)\n",
  83. cnum, delrc );
  84. } else {
  85. slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
  86. "delete_changerecord: deleted changelog entry \"%s\"\n", dnbuf);
  87. }
  88. slapi_ch_free((void **) &dnbuf );
  89. return delrc;
  90. }
  91. /*
  92. * Function: handle_getchangetime_result
  93. * Arguments: op - pointer to Operation struct for this operation
  94. * err - error code returned from search
  95. * Returns: nothing
  96. * Description: result handler for get_changetime(). Sets the crt_err
  97. * field of the cnum_result_t struct to the error returned
  98. * from the backend.
  99. */
  100. static void
  101. handle_getchangetime_result( int err, void *callback_data )
  102. {
  103. cnum_result_t *crt = callback_data;
  104. if ( crt == NULL ) {
  105. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  106. "handle_getchangetime_result: callback_data NULL\n" );
  107. } else {
  108. crt->crt_err = err;
  109. }
  110. }
  111. /*
  112. * Function: handle_getchangetime_search
  113. * Arguments: op - pointer to Operation struct for this operation
  114. * e - entry returned by backend
  115. * Returns: 0 in all cases
  116. * Description: Search result operation handler for get_changetime().
  117. * Sets fields in the cnum_result_t struct pointed to by
  118. * op->o_handler_data.
  119. */
  120. static int
  121. handle_getchangetime_search( Slapi_Entry *e, void *callback_data)
  122. {
  123. cnum_result_t *crt = callback_data;
  124. int rc;
  125. Slapi_Attr *attr;
  126. if (crt == NULL) {
  127. slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  128. "handle_getchangetime_search: op->o_handler_data NULL\n");
  129. } else if (crt->crt_nentries > 0) {
  130. /* only return the first entry, I guess */
  131. slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  132. "handle_getchangetime_search: multiple entries returned\n");
  133. } else {
  134. crt->crt_nentries++;
  135. crt->crt_time = 0;
  136. if (NULL != e) {
  137. Slapi_Value *sval = NULL;
  138. const struct berval *val = NULL;
  139. rc = slapi_entry_attr_find(e, attr_changetime, &attr);
  140. /* Bug 624442: Logic checking for lack of timestamp was
  141. reversed. */
  142. if (0 != rc || slapi_attr_first_value(attr, &sval) == -1 ||
  143. (val = slapi_value_get_berval(sval)) == NULL ||
  144. NULL == val->bv_val) {
  145. crt->crt_time = 0;
  146. } else {
  147. crt->crt_time = parse_localTime(val->bv_val);
  148. }
  149. }
  150. }
  151. return 0;
  152. }
  153. /*
  154. * Function: get_changetime
  155. * Arguments: cnum - number of change record to retrieve
  156. * Returns: Taking the attr_changetime of the 'cnum' entry,
  157. * it converts it into time_t (parse_localTime) and returns this time value.
  158. * It returns 0 in the following cases:
  159. * - changerecord entry has not attr_changetime
  160. * - attr_changetime attribute has no value
  161. * - attr_changetime attribute value is empty
  162. *
  163. * Description: Retrieve attr_changetime ("changetime") from a changerecord whose number is "cnum".
  164. */
  165. static time_t get_changetime( changeNumber cnum, int *err )
  166. {
  167. cnum_result_t crt, *crtp = &crt;
  168. char fstr[ 16 + CNUMSTR_LEN + 2 ];
  169. Slapi_PBlock *pb;
  170. if (cnum == 0UL) {
  171. if (err != NULL) {
  172. *err = LDAP_PARAM_ERROR;
  173. }
  174. return 0;
  175. }
  176. crtp->crt_nentries = crtp->crt_err = 0;
  177. crtp->crt_time = 0;
  178. PR_snprintf(fstr, sizeof (fstr), "%s=%ld", attr_changenumber, cnum);
  179. pb = slapi_pblock_new();
  180. slapi_search_internal_set_pb(pb, RETROCL_CHANGELOG_DN,
  181. LDAP_SCOPE_SUBTREE, fstr,
  182. (char **) get_cleattrs(), /* cast const */
  183. 0 /* attrsonly */,
  184. NULL /* controls */, NULL /* uniqueid */,
  185. g_plg_identity[PLUGIN_RETROCL],
  186. 0 /* actions */);
  187. slapi_search_internal_callback_pb(pb, crtp,
  188. handle_getchangetime_result,
  189. handle_getchangetime_search, NULL);
  190. if (err != NULL) {
  191. *err = crtp->crt_err;
  192. }
  193. slapi_pblock_destroy(pb);
  194. return ( crtp->crt_time);
  195. }
  196. /*
  197. * Function: trim_changelog
  198. *
  199. * Arguments: none
  200. *
  201. * Returns: 0 on success, -1 on failure
  202. *
  203. * Description: Trims the changelog, according to the constraints
  204. * described by the ts structure.
  205. */
  206. static int trim_changelog(void)
  207. {
  208. int rc = 0, ldrc, done;
  209. time_t now;
  210. changeNumber first_in_log = 0, last_in_log = 0;
  211. int num_deleted = 0;
  212. int me,lt;
  213. now = current_time();
  214. PR_Lock( ts.ts_s_trim_mutex );
  215. me = ts.ts_c_max_age;
  216. lt = ts.ts_s_last_trim;
  217. PR_Unlock( ts.ts_s_trim_mutex );
  218. if ( now - lt >= trim_interval ) {
  219. /*
  220. * Trim the changelog. Read sequentially through all the
  221. * entries, deleting any which do not meet the criteria
  222. * described in the ts structure.
  223. */
  224. done = 0;
  225. while ( !done && retrocl_trimming == 1 ) {
  226. int did_delete;
  227. did_delete = 0;
  228. first_in_log = retrocl_get_first_changenumber();
  229. if ( 0UL == first_in_log ) {
  230. slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
  231. "trim_changelog: no changelog records "
  232. "to trim\n" );
  233. /* Bail out - we can't do any useful work */
  234. break;
  235. }
  236. last_in_log = retrocl_get_last_changenumber();
  237. if ( last_in_log == first_in_log ) {
  238. /* Always leave at least one entry in the change log */
  239. break;
  240. }
  241. if ( me > 0L ) {
  242. time_t change_time = get_changetime(first_in_log, &ldrc);
  243. if (change_time) {
  244. if ((change_time + me) < now) {
  245. retrocl_set_first_changenumber(first_in_log + 1);
  246. ldrc = delete_changerecord(first_in_log);
  247. num_deleted++;
  248. did_delete = 1;
  249. }
  250. } else {
  251. /* What to do if there's no timestamp? Just delete it. */
  252. retrocl_set_first_changenumber(first_in_log + 1);
  253. ldrc = delete_changerecord(first_in_log);
  254. num_deleted++;
  255. did_delete = 1;
  256. }
  257. }
  258. if ( !did_delete ) {
  259. done = 1;
  260. }
  261. }
  262. } else {
  263. LDAPDebug(LDAP_DEBUG_PLUGIN, "not yet time to trim: %ld < (%d+%d)\n",
  264. now, lt, trim_interval);
  265. }
  266. PR_Lock( ts.ts_s_trim_mutex );
  267. ts.ts_s_trimming = 0;
  268. ts.ts_s_last_trim = now;
  269. PR_Unlock( ts.ts_s_trim_mutex );
  270. if ( num_deleted > 0 ) {
  271. slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
  272. "trim_changelog: removed %d change records\n",
  273. num_deleted );
  274. }
  275. return rc;
  276. }
  277. static int retrocl_active_threads;
  278. /*
  279. * Function: changelog_trim_thread_fn
  280. *
  281. * Returns: nothing
  282. *
  283. * Arguments: none
  284. *
  285. * Description: the thread creation callback. retrocl_active_threads is
  286. * provided for debugging purposes.
  287. *
  288. */
  289. static void
  290. changelog_trim_thread_fn( void *arg )
  291. {
  292. PR_AtomicIncrement(&retrocl_active_threads);
  293. trim_changelog();
  294. PR_AtomicDecrement(&retrocl_active_threads);
  295. }
  296. /*
  297. * Function: retrocl_housekeeping
  298. * Arguments: cur_time - the current time
  299. * Returns: nothing
  300. * Description: Determines if it is time to trim the changelog database,
  301. * and if so, determines if the changelog database needs to
  302. * be trimmed. If so, a thread is started which will trim
  303. * the database.
  304. */
  305. void retrocl_housekeeping ( time_t cur_time, void *noarg )
  306. {
  307. int ldrc;
  308. if (retrocl_be_changelog == NULL) {
  309. LDAPDebug0Args(LDAP_DEBUG_TRACE,"not housekeeping if no cl be\n");
  310. return;
  311. }
  312. if ( !ts.ts_s_initialized ) {
  313. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "changelog_housekeeping called before "
  314. "trimming constraints set\n" );
  315. return;
  316. }
  317. PR_Lock( ts.ts_s_trim_mutex );
  318. if ( !ts.ts_s_trimming ) {
  319. int must_trim = 0;
  320. /* See if we need to trim */
  321. /* Has enough time elapsed since our last check? */
  322. if ( cur_time - ts.ts_s_last_trim >= (ts.ts_c_max_age) ) {
  323. /* Is the first entry too old? */
  324. time_t first_time;
  325. /*
  326. * good we could avoid going to the database to retrieve
  327. * this time information if we cached the last value we'd read.
  328. * But a client might have deleted it over protocol.
  329. */
  330. first_time = retrocl_getchangetime( SLAPI_SEQ_FIRST, &ldrc );
  331. LDAPDebug(LDAP_DEBUG_PLUGIN,
  332. "cltrim: ldrc=%d, first_time=%ld, cur_time=%ld\n",
  333. ldrc,first_time,cur_time);
  334. if ( LDAP_SUCCESS == ldrc && first_time > (time_t) 0L &&
  335. first_time + ts.ts_c_max_age < cur_time ) {
  336. must_trim = 1;
  337. }
  338. }
  339. if ( must_trim ) {
  340. LDAPDebug0Args(LDAP_DEBUG_TRACE,"changelog about to create thread\n");
  341. /* Start a thread to trim the changelog */
  342. ts.ts_s_trimming = 1;
  343. if ( PR_CreateThread( PR_USER_THREAD,
  344. changelog_trim_thread_fn, NULL,
  345. PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
  346. RETROCL_DLL_DEFAULT_THREAD_STACKSIZE ) == NULL ) {
  347. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "unable to create changelog trimming thread\n" );
  348. }
  349. } else {
  350. LDAPDebug0Args(LDAP_DEBUG_PLUGIN,
  351. "changelog does not need to be trimmed\n");
  352. }
  353. }
  354. PR_Unlock( ts.ts_s_trim_mutex );
  355. }
  356. /*
  357. * Function: retrocl_init_trimming
  358. *
  359. * Returns: none, exits on fatal error
  360. *
  361. * Arguments: none
  362. *
  363. * Description: called during startup
  364. *
  365. */
  366. void retrocl_init_trimming (void)
  367. {
  368. const char *cl_maxage;
  369. time_t ageval = 0; /* Don't trim, by default */
  370. const char *cl_trim_interval;
  371. cl_maxage = retrocl_get_config_str(CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE);
  372. if (cl_maxage) {
  373. if (slapi_is_duration_valid(cl_maxage)) {
  374. ageval = slapi_parse_duration(cl_maxage);
  375. slapi_ch_free_string((char **)&cl_maxage);
  376. } else {
  377. slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  378. "retrocl_init_trimming: ignoring invalid %s value %s; "
  379. "not trimming retro changelog.\n",
  380. CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE, cl_maxage);
  381. slapi_ch_free_string((char **)&cl_maxage);
  382. return;
  383. }
  384. }
  385. cl_trim_interval = retrocl_get_config_str(CONFIG_CHANGELOG_TRIM_INTERVAL);
  386. if (cl_trim_interval) {
  387. trim_interval = strtol(cl_trim_interval, (char **)NULL, 10);
  388. if (0 == trim_interval) {
  389. slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  390. "retrocl_init_trimming: ignoring invalid %s value %s; "
  391. "resetting the default %d\n",
  392. CONFIG_CHANGELOG_TRIM_INTERVAL, cl_trim_interval,
  393. DEFAULT_CHANGELOGDB_TRIM_INTERVAL);
  394. trim_interval = DEFAULT_CHANGELOGDB_TRIM_INTERVAL;
  395. }
  396. slapi_ch_free_string((char **)&cl_trim_interval);
  397. }
  398. ts.ts_c_max_age = ageval;
  399. ts.ts_s_last_trim = (time_t) 0L;
  400. ts.ts_s_trimming = 0;
  401. if (( ts.ts_s_trim_mutex = PR_NewLock()) == NULL ) {
  402. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "set_changelog_trim_constraints: "
  403. "cannot create new lock.\n" );
  404. exit( 1 );
  405. }
  406. ts.ts_s_initialized = 1;
  407. retrocl_trimming = 1;
  408. retrocl_trim_ctx = slapi_eq_repeat(retrocl_housekeeping,
  409. NULL, (time_t)0,
  410. /* in milliseconds */
  411. trim_interval * 1000);
  412. }
  413. /*
  414. * Function: retrocl_stop_trimming
  415. *
  416. * Returns: none
  417. *
  418. * Arguments: none
  419. *
  420. * Description: called when server is shutting down to ensure trimming stops
  421. * eventually.
  422. *
  423. */
  424. void retrocl_stop_trimming(void)
  425. {
  426. retrocl_trimming = 0;
  427. if (retrocl_trim_ctx) {
  428. slapi_eq_cancel(retrocl_trim_ctx);
  429. retrocl_trim_ctx = NULL;
  430. }
  431. }