s4-dsdb: Try to avoid much of the time a db search for msDS-IntID
authorMatthieu Patou <mat@matws.net>
Sat, 12 May 2012 09:13:42 +0000 (02:13 -0700)
committerMatthieu Patou <mat@matws.net>
Sat, 23 Jun 2012 06:22:02 +0000 (23:22 -0700)
We search in the schema if we have already this intid (using dsdb_attribute_by_attributeID_id because
in the range 0x80000000 0xBFFFFFFFF, attributeID is a DSDB_ATTID_TYPE_INTID).
If so generate another random value.
If not check if the highest USN in the database for the schema partition is the
one that we know.
If so it means that's only this ldb context that is touching the schema in the database.
If not it means that's someone else has modified the database while we are doing our changes too
(this case should be very bery rare) in order to be sure do the search in the database.

source4/dsdb/samdb/ldb_modules/repl_meta_data.c
source4/dsdb/samdb/ldb_modules/samldb.c
source4/dsdb/samdb/samdb.h

index a558a64999ef146764b65a71f9668a06b2005f33..49fca5f376ecd44542d68f3da2af3a3525831eed 100644 (file)
@@ -751,6 +751,7 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
  */
 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
 {
+       struct samldb_msds_intid_persistant *msds_intid_struct;
        struct ldb_context *ldb;
         struct ldb_control *control;
        struct replmd_replicated_request *ac;
@@ -1053,7 +1054,14 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        if (control) {
                control->critical = 0;
        }
+       if (ldb_dn_compare_base(ac->schema->base_dn, req->op.add.message->dn) != 0) {
 
+               /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
+               msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
+               if (msds_intid_struct) {
+                       msds_intid_struct->usn = ac->seq_num;
+               }
+       }
        /* go on with the call chain */
        return ldb_next_request(module, down_req);
 }
@@ -2294,6 +2302,7 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
 
 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
 {
+       struct samldb_msds_intid_persistant *msds_intid_struct;
        struct ldb_context *ldb;
        struct replmd_replicated_request *ac;
        struct ldb_request *down_req;
@@ -2426,6 +2435,14 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
                }
        }
 
+       if (!ldb_dn_compare_base(ac->schema->base_dn, msg->dn)) {
+               /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
+               msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
+               if (msds_intid_struct) {
+                       msds_intid_struct->usn = ac->seq_num;
+               }
+       }
+
        /* go on with the call chain */
        return ldb_next_request(module, down_req);
 }
index 7b98dd62c1fe2d6c319b58590a3a1f573d2081ec..a859fc9c73f58996c2d22e8a17d922512ece7d84 100644 (file)
@@ -462,36 +462,95 @@ static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
        if (system_flags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) {
                return LDB_SUCCESS;
        }
+       schema = dsdb_get_schema(ldb, NULL);
+       if (!schema) {
+               ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+                             "samldb_schema_info_update: no dsdb_schema loaded");
+               DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
+               return ldb_operr(ldb);
+       }
 
-       /* Generate new value for msDs-IntId
-        * Value should be in 0x80000000..0xBFFFFFFF range */
-       msds_intid = generate_random() % 0X3FFFFFFF;
-       msds_intid += 0x80000000;
+       msds_intid_struct = (struct samldb_msds_intid_persistant*) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
+       if (!msds_intid_struct) {
+               msds_intid_struct = talloc(ldb, struct samldb_msds_intid_persistant);
+               /* Generate new value for msDs-IntId
+               * Value should be in 0x80000000..0xBFFFFFFF range */
+               msds_intid = generate_random() % 0X3FFFFFFF;
+               msds_intid += 0x80000000;
+               msds_intid_struct->msds_intid = msds_intid;
+               msds_intid_struct->usn = schema->loaded_usn;
+               DEBUG(2, ("No samldb_msds_intid_persistant struct, allocating a new one\n"));
+       } else {
+               msds_intid = msds_intid_struct->msds_intid;
+       }
 
        /* probe id values until unique one is found */
        do {
+               uint64_t current_usn;
                msds_intid++;
                if (msds_intid > 0xBFFFFFFF) {
                        msds_intid = 0x80000001;
                }
+               /*
+                * Alternative strategy to a costly (even indexed search) to the
+                * database.
+                * We search in the schema if we have already this intid (using dsdb_attribute_by_attributeID_id because
+                * in the range 0x80000000 0xBFFFFFFFF, attributeID is a DSDB_ATTID_TYPE_INTID).
+                * If so generate another random value.
+                * If not check if the highest USN in the database for the schema partition is the
+                * one that we know.
+                * If so it means that's only this ldb context that is touching the schema in the database.
+                * If not it means that's someone else has modified the database while we are doing our changes too
+                * (this case should be very bery rare) in order to be sure do the search in the database.
+                */
+               if (dsdb_attribute_by_attributeID_id(schema, msds_intid)) {
+                       msds_intid = generate_random() % 0X3FFFFFFF;
+                       msds_intid += 0x80000000;
+                       continue;
+               }
 
-               ret = dsdb_module_search(ac->module, ac,
-                                        &ldb_res,
-                                        schema_dn, LDB_SCOPE_ONELEVEL, NULL,
-                                        DSDB_FLAG_NEXT_MODULE,
-                                        ac->req,
-                                        "(msDS-IntId=%d)", msds_intid);
+               ret = dsdb_module_load_partition_usn(ac->module, schema->base_dn, &current_usn, NULL, NULL);
                if (ret != LDB_SUCCESS) {
                        ldb_debug_set(ldb, LDB_DEBUG_ERROR,
-                                     __location__": Searching for msDS-IntId=%d failed - %s\n",
-                                     msds_intid,
+                                     __location__": Searching for schema USN failed: %s\n",
                                      ldb_errstring(ldb));
                        return ldb_operr(ldb);
                }
-               id_exists = (ldb_res->count > 0);
 
-               talloc_free(ldb_res);
+               /* current_usn can be lesser than msds_intid_struct-> if there is
+                * uncommited changes.
+                */
+               if (current_usn > msds_intid_struct->usn) {
+                       /* oups something has changed, someone/something
+                        * else is modifying or has modified the schema
+                        * we'd better check this intid is the database directly
+                        */
+
+                       DEBUG(2, ("Schema has changed, searching the database for the unicity of %d\n",
+                                       msds_intid));
+
+                       ret = dsdb_module_search(ac->module, ac,
+                                               &ldb_res,
+                                               schema_dn, LDB_SCOPE_ONELEVEL, NULL,
+                                               DSDB_FLAG_NEXT_MODULE,
+                                               ac->req,
+                                               "(msDS-IntId=%d)", msds_intid);
+                       if (ret != LDB_SUCCESS) {
+                               ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+                                       __location__": Searching for msDS-IntId=%d failed - %s\n",
+                                       msds_intid,
+                                       ldb_errstring(ldb));
+                               return ldb_operr(ldb);
+                       }
+                       id_exists = (ldb_res->count > 0);
+                       talloc_free(ldb_res);
+               } else {
+                       id_exists = 0;
+               }
+
        } while(id_exists);
+       msds_intid_struct->msds_intid = msds_intid;
+       ldb_set_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE, msds_intid_struct);
 
        return samdb_msg_add_int(ldb, ac->msg, ac->msg, "msDS-IntId",
                                 msds_intid);
index 2a4bd355eb5fe93325e68e51c8162ce87bf670e7..73da3e20cfcac83c70ccd1289b1960c2b081bbe4 100644 (file)
@@ -191,6 +191,13 @@ struct dsdb_openldap_dereference_result_control {
        struct dsdb_openldap_dereference_result **attributes;
 };
 
+struct samldb_msds_intid_persistant {
+       uint32_t msds_intid;
+       uint64_t usn;
+};
+
+#define SAMLDB_MSDS_INTID_OPAQUE "SAMLDB_MSDS_INTID_OPAQUE"
+
 #define DSDB_PARTITION_DN "@PARTITION"
 #define DSDB_PARTITION_ATTR "partition"