dsdb-schema: do not reload more often than schema_reload_interval
authorMatthieu Patou <mat@matws.net>
Thu, 10 May 2012 05:08:55 +0000 (22:08 -0700)
committerMatthieu Patou <mat@matws.net>
Sat, 23 Jun 2012 06:16:04 +0000 (23:16 -0700)
Samba 4 use to try to reload the schema every time dsdb_get_schema was
called (which could be 20+ time per ldb request). Now we only reload at
most every xx seconds (xx being the value of dsdb:"schema_reload_interval"
 or 120). The timestamp of the last reloaded schema is kept in the
 dsdb_schema object. There is also a timestamp in the ldb_context, that
 is used by the LDAP server to know if it has to reload the schema after
 handling the request. This is used to allow that the schema will be
 immediately reload after a schemaUpdateNow request has been issued, the
 reload can't occur in the handling of the LDAP request itself because
 we have a transaction autostarted.

source4/dsdb/samdb/ldb_modules/samldb.c
source4/dsdb/samdb/ldb_modules/schema_load.c
source4/dsdb/samdb/samdb.h
source4/dsdb/schema/schema.h
source4/ldap_server/ldap_backend.c

index 41c97bf32a4693c8adc1c88ff3cfed456191e23c..7b98dd62c1fe2d6c319b58590a3a1f573d2081ec 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
    Copyright (C) Simo Sorce  2004-2008
    Copyright (C) Matthias Dieter Wallnöfer 2009-2011
+   Copyright (C) Matthieu Patou 2012
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -428,6 +429,8 @@ static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
        struct ldb_context *ldb;
        struct ldb_result *ldb_res;
        struct ldb_dn *schema_dn;
+       struct samldb_msds_intid_persistant *msds_intid_struct;
+       struct dsdb_schema *schema;
 
        ldb = ldb_module_get_ctx(ac->module);
        schema_dn = ldb_get_schema_basedn(ldb);
index 4c166d51ce5f76d0bab2b2fd16ce3cc6822a7321..f922aabba90e2e96c02b5245e56bf505fcd7e935 100644 (file)
@@ -49,6 +49,10 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
        struct dsdb_control_current_partition *ctrl;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct dsdb_schema *new_schema;
+       int interval;
+       time_t ts, lastts;
+       struct loadparm_context *lp_ctx =
+               (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
        
        struct schema_load_private_data *private_data = talloc_get_type(ldb_module_get_private(module), struct schema_load_private_data);
        if (!private_data) {
@@ -61,6 +65,14 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
                return schema;
        }
 
+       lastts = schema->last_refresh;
+       ts = time(NULL);
+       interval = lpcfg_parm_int(lp_ctx, NULL, "dsdb", "schema_reload_interval", 120);
+       if (lastts > (ts - interval)) {
+               DEBUG(11, ("Less than %d seconds since last reload, returning cached version ts = %d\n", interval, (int)lastts));
+               return schema;
+       }
+
        res = talloc_zero(schema, struct ldb_result);
        if (res == NULL) {
                return NULL;
@@ -84,7 +96,16 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
                talloc_free(res);
                return NULL;
        }
-       
+
+       /*
+        * We update right now the last refresh timestamp so that if
+        * the schema partition hasn't change we don't keep on retrying.
+        * Otherwise if the timestamp was update only when the schema has
+        * actually changed (and therefor completely reloaded) we would
+        * continue to hit the database to get the highest USN.
+        */
+       schema->last_refresh = ts;
+
        ctrl = talloc(treq, struct dsdb_control_current_partition);
        if (!ctrl) {
                talloc_free(res);
@@ -130,7 +151,7 @@ static struct dsdb_schema *dsdb_schema_refresh(struct ldb_module *module, struct
        if (ret != LDB_SUCCESS) {
                return schema;
        }
-       
+
        if (is_global_schema) {
                dsdb_make_schema_global(ldb, new_schema);
        }
@@ -226,6 +247,7 @@ static int dsdb_schema_from_db(struct ldb_module *module, struct ldb_dn *schema_
        ret = dsdb_set_schema(ldb, (*schema));
 
        (*schema)->refresh_in_progress = false;
+       (*schema)->last_refresh = time(NULL);
 
        if (ret != LDB_SUCCESS) {
                ldb_debug_set(ldb, LDB_DEBUG_FATAL,
@@ -324,9 +346,22 @@ static int schema_load_del_transaction(struct ldb_module *module)
 
 static int schema_load_extended(struct ldb_module *module, struct ldb_request *req)
 {
+       time_t *lastts;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct dsdb_schema *schema;
+
        if (strcmp(req->op.extended.oid, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID) != 0) {
                return ldb_next_request(module, req);
        }
+       lastts = (time_t *)ldb_get_opaque(ldb, DSDB_OPAQUE_LAST_SCHEMA_UPDATE_MSG_OPAQUE_NAME);
+       if (!lastts) {
+               lastts = talloc(ldb, time_t);
+       }
+       schema = dsdb_get_schema(ldb, NULL);
+       /* Force a refresh */
+       schema->last_refresh = 0;
+       *lastts = 0;
+       ldb_set_opaque(ldb, DSDB_OPAQUE_LAST_SCHEMA_UPDATE_MSG_OPAQUE_NAME, lastts);
 
        /* This is a no-op.  We reload as soon as we can */
        return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
index 50f7bf7d13ae01b14d2d37b1e2f2955ee66fea3d..2a4bd355eb5fe93325e68e51c8162ce87bf670e7 100644 (file)
@@ -199,6 +199,7 @@ struct dsdb_extended_dn_store_format {
        bool store_extended_dn_in_ldb;
 };
 
+#define DSDB_OPAQUE_LAST_SCHEMA_UPDATE_MSG_OPAQUE_NAME "DSDB_OPAQUE_LAST_SCHEMA_UPDATE"
 #define DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME "DSDB_OPAQUE_PARTITION_MODULE_MSG"
 
 /* this takes a struct dsdb_fsmo_extended_op */
index b1ae76882ab035733c62da1bae7b90ae31a9876a..d3dd035e0132b41e7656275d8c8c07172e8362ac 100644 (file)
@@ -245,6 +245,7 @@ struct dsdb_schema {
        struct ldb_module *loaded_from_module;
        struct dsdb_schema *(*refresh_fn)(struct ldb_module *module, struct dsdb_schema *schema, bool is_global_schema);
        bool refresh_in_progress;
+       time_t last_refresh;
        /* an 'opaque' sequence number that the reload function may also wish to use */
        uint64_t reload_seq_number;
 
index afeb3500d0fe7e378f93556367f3cc385957f506..000d7e28f8144b2baf4c7983f8ab1a1950c66507 100644 (file)
@@ -1162,6 +1162,9 @@ NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
 {
        unsigned int i;
        struct ldap_message *msg = call->request;
+       struct ldb_context *samdb = call->conn->ldb;
+       NTSTATUS status;
+       time_t *lastts;
        /* Check for undecoded critical extensions */
        for (i=0; msg->controls && msg->controls[i]; i++) {
                if (!msg->controls_decoded[i] && 
@@ -1180,9 +1183,11 @@ NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
        case LDAP_TAG_SearchRequest:
                return ldapsrv_SearchRequest(call);
        case LDAP_TAG_ModifyRequest:
-               return ldapsrv_ModifyRequest(call);
+               status = ldapsrv_ModifyRequest(call);
+               break;
        case LDAP_TAG_AddRequest:
-               return ldapsrv_AddRequest(call);
+               status = ldapsrv_AddRequest(call);
+               break;
        case LDAP_TAG_DelRequest:
                return ldapsrv_DelRequest(call);
        case LDAP_TAG_ModifyDNRequest:
@@ -1196,4 +1201,20 @@ NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
        default:
                return ldapsrv_unwilling(call, LDAP_PROTOCOL_ERROR);
        }
+
+       if (NT_STATUS_IS_OK(status)) {
+               lastts = (time_t *)ldb_get_opaque(samdb, DSDB_OPAQUE_LAST_SCHEMA_UPDATE_MSG_OPAQUE_NAME);
+               if (lastts && !*lastts) {
+                       DEBUG(10, ("Schema update now was requested, fullfilling the request ts = %d\n", lastts));
+                       /*
+                       * Just requesting the schema will do the trick
+                       * as the delay for reload is experied, we will have a reload
+                       * from the schema as expected as we are not yet in a transaction!
+                       */
+                       dsdb_get_schema(samdb, NULL);
+                       *lastts = time(NULL);
+                       ldb_set_opaque(samdb, DSDB_OPAQUE_LAST_SCHEMA_UPDATE_MSG_OPAQUE_NAME, lastts);
+               }
+       }
+       return status;
 }