[ClusterLabs Developers] Pacemaker
Владимир Лямин
st067898 at student.spbu.ru
Sat Jan 7 20:41:41 UTC 2023
Hello, I'm Lyamin Vladimir. First-year master of St. Petersburg State
University. I decided to optimize the running time of the
pcmk__unpack_constraints function, since there is a loop over all the data
here. I decided to use a hash table to optimize this.
A hash table structure has been added, as well as functions to manage it.
pe_resource_t* compareKey(const char* key, struct set *array);
int getHash(const char *S);
void push(Node **head, pe_resource_t* data);
void insert(char* key, pe_resource_t* data, struct set *array);
void init_array(struct set **array);
void insert_children(pe_resource_t * rsc, struct set *hashTable);
Existing functions have also been changed: pcmk__unpack_constraints
(initialization of the hash table) and pcmk__find_constraint_resource
(search for the desired resource)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.clusterlabs.org/pipermail/developers/attachments/20230107/1c118f58/attachment-0001.htm>
-------------- next part --------------
/*
* Copyright 2004-2021 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdbool.h>
#include <regex.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/common/iso8601.h>
#include <crm/pengine/status.h>
#include <crm/pengine/internal.h>
#include <crm/pengine/rules.h>
#include <pacemaker-internal.h>
#include "libpacemaker_private.h"
#include <stdio.h>
typedef struct Node {
pe_resource_t* value;
struct Node *next;
} Node;
struct set
{
int key;
Node* head;
};
int capacity = 200;
struct set *hashTable = NULL;
pe_resource_t *
compareKey(const char* key, struct set *array);
int
getHash(const char *S);
void
push(Node **head, pe_resource_t* data);
void
insert(char* key, pe_resource_t* data, struct set *array);
void
init_array(struct set **array);
void
free_item(Node *item);
void
free_table(struct set *array);
void
insert_children(pe_resource_t * rsc, struct set *hashTable);
static bool
evaluate_lifetime(xmlNode *lifetime, pe_working_set_t *data_set)
{
bool result = FALSE;
crm_time_t *next_change = crm_time_new_undefined();
result = pe_evaluate_rules(lifetime, NULL, data_set->now, next_change);
if (crm_time_is_defined(next_change)) {
time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
pe__update_recheck_time(recheck, data_set);
}
crm_time_free(next_change);
return result;
}
void
push(Node **head, pe_resource_t* data) {
Node *tmp = (Node *) malloc(sizeof(Node));
tmp->value = data;
tmp->next = (*head);
(*head) = tmp;
}
int
getHash(const char *S)
{
int i = 0;
int r = 0;
while(*S)
{
i++;
r+=(int)(*S);
S = S + 3;
}
return r % capacity;
}
void
init_array(struct set **array)
{
struct set *tmp = (struct set *) malloc(capacity * sizeof(struct set));
for (int i = 0; i < capacity; i++)
{
tmp[i].key = i;
tmp[i].head = NULL;
}
(*array) = tmp;
}
void
free_item(Node *item) {
free(item);
}
void
free_table(struct set *array) {
for (int i=0; i <capacity; i++) {
Node *item = array[i].head;
while (item != NULL) {
Node *tmp = item->next;
free_item(item);
item = tmp;
}
}
free(array);
}
void
insert(char* key, pe_resource_t* data, struct set *array)
{
int index = getHash(key);
if (array[index].head == NULL)
{
Node *head = NULL;
array[index].key = index;
push(&head, data);
array[index].head = head;
}
else if (array[index].key == index)
{
push(&array[index].head, data);
}
}
pe_resource_t *
compareKey(const char* key, struct set *array)
{
int index = getHash(key);
if (array[index].head == 0)
{
printf("\n Key do not exists \n");
}
else
{
Node *ptr = array[index].head;
while (ptr != NULL)
{
if (!strcmp(key, ptr->value->id)) {
return ptr->value;
}
ptr = ptr->next;
}
return NULL;
}
return NULL;
}
void
insert_children(pe_resource_t * rsc, struct set *hashTable) {
for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
pe_resource_t *child = (pe_resource_t *) gIter->data;
insert(child->id, child, hashTable);
insert_children(child, hashTable);
}
}
/*!
* \internal
* \brief Unpack constraints from XML
*
* Given a cluster working set, unpack all constraints from its input XML into
* data structures.
*
* \param[in,out] data_set Cluster working set
*/
void
pcmk__unpack_constraints(pe_working_set_t *data_set)
{
xmlNode *xml_constraints = pcmk_find_cib_element(data_set->input,
XML_CIB_TAG_CONSTRAINTS);
GList *rIter = NULL;
init_array(&hashTable);
for (rIter = data_set->resources; rIter; rIter = rIter->next) {
pe_resource_t *parent = rIter->data;
insert(parent->id, parent, hashTable);
insert_children(parent, hashTable);
}
for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints);
xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) {
xmlNode *lifetime = NULL;
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *tag = crm_element_name(xml_obj);
if (id == NULL) {
pcmk__config_err("Ignoring <%s> constraint without "
XML_ATTR_ID, tag);
continue;
}
crm_trace("Unpacking %s constraint '%s'", tag, id);
lifetime = first_named_child(xml_obj, "lifetime");
if (lifetime != NULL) {
pcmk__config_warn("Support for 'lifetime' attribute (in %s) is "
"deprecated (the rules it contains should "
"instead be direct descendents of the "
"constraint object)", id);
}
if ((lifetime != NULL) && !evaluate_lifetime(lifetime, data_set)) {
crm_info("Constraint %s %s is not active", tag, id);
} else if (pcmk__str_eq(XML_CONS_TAG_RSC_ORDER, tag, pcmk__str_casei)) {
pcmk__unpack_ordering(xml_obj, data_set);
} else if (pcmk__str_eq(XML_CONS_TAG_RSC_DEPEND, tag, pcmk__str_casei)) {
pcmk__unpack_colocation(xml_obj, data_set);
} else if (pcmk__str_eq(XML_CONS_TAG_RSC_LOCATION, tag, pcmk__str_casei)) {
pcmk__unpack_location(xml_obj, data_set);
} else if (pcmk__str_eq(XML_CONS_TAG_RSC_TICKET, tag, pcmk__str_casei)) {
pcmk__unpack_rsc_ticket(xml_obj, data_set);
} else {
pe_err("Unsupported constraint type: %s", tag);
}
}
free_table(hashTable);
}
pe_resource_t *
pcmk__find_constraint_resource(GList *rsc_list, const char *id)
{
pe_resource_t *match = compareKey(id, hashTable);
if(!pcmk__str_eq(match->id, id, pcmk__str_casei)) {
/* We found an instance of a clone instead */
match = uber_parent(match);
crm_debug("Found %s for %s", match->id, id);
}
return match;
}
/*!
* \internal
* \brief Check whether an ID references a resource tag
*
* \param[in] data_set Cluster working set
* \param[in] id Tag ID to search for
* \param[out] tag Where to store tag, if found
*
* \return true if ID refers to a tagged resource or resource set template,
* otherwise false
*/
static bool
find_constraint_tag(pe_working_set_t *data_set, const char *id, pe_tag_t **tag)
{
*tag = NULL;
// Check whether id refers to a resource set template
if (g_hash_table_lookup_extended(data_set->template_rsc_sets, id,
NULL, (gpointer *) tag)) {
if (*tag == NULL) {
crm_warn("No resource is derived from template '%s'", id);
return false;
}
return true;
}
// If not, check whether id refers to a tag
if (g_hash_table_lookup_extended(data_set->tags, id,
NULL, (gpointer *) tag)) {
if (*tag == NULL) {
crm_warn("No resource is tagged with '%s'", id);
return false;
}
return true;
}
crm_warn("No template or tag named '%s'", id);
return false;
}
/*!
* \brief
* \internal Check whether an ID refers to a valid resource or tag
*
* \param[in] data_set Cluster working set
* \param[in] id ID to search for
* \param[out] rsc Where to store resource, if found (or NULL to skip
* searching resources)
* \param[out] tag Where to store tag, if found (or NULL to skip searching
* tags)
*
* \return true if id refers to a resource (possibly indirectly via a tag)
*/
bool
pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id,
pe_resource_t **rsc, pe_tag_t **tag)
{
if (rsc != NULL) {
*rsc = pcmk__find_constraint_resource(data_set->resources, id);
if (*rsc != NULL) {
return true;
}
}
if ((tag != NULL) && find_constraint_tag(data_set, id, tag)) {
return true;
}
return false;
}
/*!
* \internal
* \brief Replace any resource tags with equivalent resource_ref entries
*
* If a given constraint has resource sets, check each set for resource_ref
* entries that list tags rather than resource IDs, and replace any found with
* resource_ref entries for the corresponding resource IDs.
*
* \param[in] xml_obj Constraint XML
* \param[in] data_set Cluster working set
*
* \return Equivalent XML with resource tags replaced (or NULL if none)
* \note It is the caller's responsibility to free the result with free_xml().
*/
xmlNode *
pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
{
xmlNode *new_xml = NULL;
bool any_refs = false;
// Short-circuit if there are no sets
if (first_named_child(xml_obj, XML_CONS_TAG_RSC_SET) == NULL) {
return NULL;
}
new_xml = copy_xml(xml_obj);
for (xmlNode *set = first_named_child(new_xml, XML_CONS_TAG_RSC_SET);
set != NULL; set = crm_next_same_xml(set)) {
GList *tag_refs = NULL;
GList *gIter = NULL;
for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
pe_resource_t *rsc = NULL;
pe_tag_t *tag = NULL;
if (!pcmk__valid_resource_or_tag(data_set, ID(xml_rsc), &rsc,
&tag)) {
pcmk__config_err("Ignoring resource sets for constraint '%s' "
"because '%s' is not a valid resource or tag",
ID(xml_obj), ID(xml_rsc));
free_xml(new_xml);
return NULL;
} else if (rsc) {
continue;
} else if (tag) {
/* The resource_ref under the resource_set references a template/tag */
xmlNode *last_ref = xml_rsc;
/* A sample:
Original XML:
<resource_set id="tag1-colocation-0" sequential="true">
<resource_ref id="rsc1"/>
<resource_ref id="tag1"/>
<resource_ref id="rsc4"/>
</resource_set>
Now we are appending rsc2 and rsc3 which are tagged with tag1 right after it:
<resource_set id="tag1-colocation-0" sequential="true">
<resource_ref id="rsc1"/>
<resource_ref id="tag1"/>
<resource_ref id="rsc2"/>
<resource_ref id="rsc3"/>
<resource_ref id="rsc4"/>
</resource_set>
*/
for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
const char *obj_ref = (const char *) gIter->data;
xmlNode *new_rsc_ref = NULL;
new_rsc_ref = xmlNewDocRawNode(getDocPtr(set), NULL,
(pcmkXmlStr) XML_TAG_RESOURCE_REF, NULL);
crm_xml_add(new_rsc_ref, XML_ATTR_ID, obj_ref);
xmlAddNextSibling(last_ref, new_rsc_ref);
last_ref = new_rsc_ref;
}
any_refs = true;
/* Freeing the resource_ref now would break the XML child
* iteration, so just remember it for freeing later.
*/
tag_refs = g_list_append(tag_refs, xml_rsc);
}
}
/* Now free '<resource_ref id="tag1"/>', and finally get:
<resource_set id="tag1-colocation-0" sequential="true">
<resource_ref id="rsc1"/>
<resource_ref id="rsc2"/>
<resource_ref id="rsc3"/>
<resource_ref id="rsc4"/>
</resource_set>
*/
for (gIter = tag_refs; gIter != NULL; gIter = gIter->next) {
xmlNode *tag_ref = gIter->data;
free_xml(tag_ref);
}
g_list_free(tag_refs);
}
if (!any_refs) {
free_xml(new_xml);
new_xml = NULL;
}
return new_xml;
}
/*!
* \internal
* \brief Convert a tag into a resource set of tagged resources
*
* \param[in] xml_obj Constraint XML
* \param[out] rsc_set Where to store resource set XML created based on tag
* \param[in] attr Name of XML attribute containing resource or tag ID
* \param[in] convert_rsc Convert to set even if \p attr references a resource
* \param[in] data_set Cluster working set
*/
bool
pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
bool convert_rsc, pe_working_set_t *data_set)
{
const char *cons_id = NULL;
const char *id = NULL;
pe_resource_t *rsc = NULL;
pe_tag_t *tag = NULL;
*rsc_set = NULL;
CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false);
cons_id = ID(xml_obj);
if (cons_id == NULL) {
pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
crm_element_name(xml_obj));
return false;
}
id = crm_element_value(xml_obj, attr);
if (id == NULL) {
return true;
}
if (!pcmk__valid_resource_or_tag(data_set, id, &rsc, &tag)) {
pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
"valid resource or tag", cons_id, id);
return false;
} else if (tag) {
GList *gIter = NULL;
/* A template/tag is referenced by the "attr" attribute (first, then, rsc or with-rsc).
Add the template/tag's corresponding "resource_set" which contains the resources derived
from it or tagged with it under the constraint. */
*rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
crm_xml_add(*rsc_set, XML_ATTR_ID, id);
for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
const char *obj_ref = (const char *) gIter->data;
xmlNode *rsc_ref = NULL;
rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
crm_xml_add(rsc_ref, XML_ATTR_ID, obj_ref);
}
/* Set sequential="false" for the resource_set */
pcmk__xe_set_bool_attr(*rsc_set, "sequential", false);
} else if ((rsc != NULL) && convert_rsc) {
/* Even a regular resource is referenced by "attr", convert it into a resource_set.
Because the other side of the constraint could be a template/tag reference. */
xmlNode *rsc_ref = NULL;
*rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
crm_xml_add(*rsc_set, XML_ATTR_ID, id);
rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
crm_xml_add(rsc_ref, XML_ATTR_ID, id);
} else {
return true;
}
/* Remove the "attr" attribute referencing the template/tag */
if (*rsc_set != NULL) {
xml_remove_prop(xml_obj, attr);
}
return true;
}
/*!
* \internal
* \brief Create constraints inherent to resource types
*
* \param[in,out] data_set Cluster working set
*/
void
pcmk__create_internal_constraints(pe_working_set_t *data_set)
{
crm_trace("Create internal constraints");
for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
pe_resource_t *rsc = (pe_resource_t *) iter->data;
rsc->cmds->internal_constraints(rsc);
}
}
More information about the Developers
mailing list