Skip to content
Permalink
Browse files
VLE memory usage bug fixes
Added some bug fixes to the VLE memory management system.

Moved the age_vertex_stats function to global_graph.
  • Loading branch information
jrgemignani committed Apr 1, 2022
1 parent e4205f0 commit b558c7023997dd8f7342a98f55353b7647f6edfd
Showing 3 changed files with 327 additions and 119 deletions.
@@ -3924,6 +3924,13 @@ STABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';

CREATE FUNCTION ag_catalog.age_delete_global_graphs(agtype)
RETURNS boolean
LANGUAGE c
STABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';

--
-- End
--
@@ -87,6 +87,8 @@ static GRAPH_global_context *global_graph_contexts = NULL;
/* declarations */
/* GRAPH global context functions */
static void free_specific_GRAPH_global_context(GRAPH_global_context *ggctx);
static bool delete_specific_GRAPH_global_contexts(char *graph_name);
static bool delete_GRAPH_global_contexts(void);
static void create_GRAPH_global_hashtables(GRAPH_global_context *ggctx);
static void load_GRAPH_global_hashtables(GRAPH_global_context *ggctx);
static void load_vertex_hashtable(GRAPH_global_context *ggctx);
@@ -146,6 +148,7 @@ static void create_GRAPH_global_hashtables(GRAPH_global_context *ggctx)
ggctx->vertex_hashtable = hash_create(vhn, VERTEX_HTAB_INITIAL_SIZE,
&vertex_ctl,
HASH_ELEM | HASH_FUNCTION);
pfree(vhn);

/* initialize the edge hashtable */
MemSet(&edge_ctl, 0, sizeof(edge_ctl));
@@ -154,6 +157,7 @@ static void create_GRAPH_global_hashtables(GRAPH_global_context *ggctx)
edge_ctl.hash = tag_hash;
ggctx->edge_hashtable = hash_create(ehn, EDGE_HTAB_INITIAL_SIZE, &edge_ctl,
HASH_ELEM | HASH_FUNCTION);
pfree(ehn);
}

/* helper function to get a List of all label names for the specified graph */
@@ -573,6 +577,10 @@ static void free_specific_GRAPH_global_context(GRAPH_global_context *ggctx)

/* free the graph name */
pfree(ggctx->graph_name);
ggctx->graph_name = NULL;

ggctx->graph_oid = InvalidOid;
ggctx->next = NULL;

/* free the vertex edge lists, starting with the head */
curr_vertex = peek_stack_head(ggctx->vertices);
@@ -601,19 +609,28 @@ static void free_specific_GRAPH_global_context(GRAPH_global_context *ggctx)
free_ListGraphId(value->edges_out);
free_ListGraphId(value->edges_self);

value->edges_in = NULL;
value->edges_out = NULL;
value->edges_self = NULL;

/* move to the next vertex */
curr_vertex = next_vertex;
}

/* free the vertices list */
free_ListGraphId(ggctx->vertices);
ggctx->vertices = NULL;

/* free the hashtables */
hash_destroy(ggctx->vertex_hashtable);
hash_destroy(ggctx->edge_hashtable);

ggctx->vertex_hashtable = NULL;
ggctx->edge_hashtable = NULL;

/* free the context */
pfree(ggctx);
ggctx = NULL;
}

/*
@@ -732,6 +749,95 @@ GRAPH_global_context *manage_GRAPH_global_contexts(char *graph_name,
return new_ggctx;
}

/*
* Helper function to delete all of the global graph contexts used by the
* process. When done the global global_graph_contexts will be NULL.
*/
static bool delete_GRAPH_global_contexts(void)
{
GRAPH_global_context *curr_ggctx = NULL;
bool retval = false;

/* get the first context, if any */
curr_ggctx = global_graph_contexts;

/* free all GRAPH global contexts */
while (curr_ggctx != NULL)
{
GRAPH_global_context *next_ggctx = curr_ggctx->next;

/* free the current graph context */
free_specific_GRAPH_global_context(curr_ggctx);

/* advance to the next context */
curr_ggctx = next_ggctx;

retval = true;
}

/* clear the global variable */
global_graph_contexts = NULL;

return retval;
}

/*
* Helper function to delete a specific global graph context used by the
* process.
*/
static bool delete_specific_GRAPH_global_contexts(char *graph_name)
{
GRAPH_global_context *prev_ggctx = NULL;
GRAPH_global_context *curr_ggctx = NULL;
Oid graph_oid = InvalidOid;

if (graph_name == NULL)
{
return false;
}

/* get the graph oid */
graph_oid = get_graph_oid(graph_name);

/* get the first context, if any */
curr_ggctx = global_graph_contexts;

/* find the specified GRAPH global context */
while (curr_ggctx != NULL)
{
GRAPH_global_context *next_ggctx = curr_ggctx->next;

if (curr_ggctx->graph_oid == graph_oid)
{
/*
* If prev_ggctx is NULL then we are freeing the top of the
* contexts. So, we need to point the global variable to the
* new (next) top context, if there is one.
*/
if (prev_ggctx == NULL)
{
global_graph_contexts = next_ggctx;
}
else
{
prev_ggctx->next = curr_ggctx->next;
}

/* free the current graph context */
free_specific_GRAPH_global_context(curr_ggctx);

/* we found and freed it, return true */
return true;
}

/* advance to the next one */
curr_ggctx = next_ggctx;
}

/* we didn't find it, return false */
return false;
}

/*
* Helper function to retrieve a vertex_entry from the graph's vertex hash
* table. If there isn't one, it returns a NULL. The latter is necessary for
@@ -853,3 +959,160 @@ graphid get_edge_entry_end_vertex_id(edge_entry *ee)
{
return ee->end_vertex_id;
}

/* PostgreSQL SQL facing functions */

/* PG wrapper function for age_delete_global_graphs */
PG_FUNCTION_INFO_V1(age_delete_global_graphs);

Datum age_delete_global_graphs(PG_FUNCTION_ARGS)
{
agtype_value *agtv_temp = NULL;
bool success = false;

/* get the graph name if supplied */
if (!PG_ARGISNULL(0))
{
agtv_temp = get_agtype_value("delete_global_graphs",
AG_GET_ARG_AGTYPE_P(0),
AGTV_STRING, false);
}

if (agtv_temp == NULL || agtv_temp->type == AGTV_NULL)
{
success = delete_GRAPH_global_contexts();
}
else if (agtv_temp->type == AGTV_STRING)
{
char *graph_name = NULL;

graph_name = agtv_temp->val.string.val;
success = delete_specific_GRAPH_global_contexts(graph_name);
}
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("delete_global_graphs: invalid graph name type")));
}

PG_RETURN_BOOL(success);
}

/* PG wrapper function for age_vertex_degree */
PG_FUNCTION_INFO_V1(age_vertex_stats);

Datum age_vertex_stats(PG_FUNCTION_ARGS)
{
GRAPH_global_context *ggctx = NULL;
vertex_entry *ve = NULL;
ListGraphId *edges = NULL;
agtype_value *agtv_vertex = NULL;
agtype_value *agtv_temp = NULL;
agtype_value agtv_integer;
agtype_in_state result;
char *graph_name = NULL;
Oid graph_oid = InvalidOid;
graphid vid = 0;
int64 self_loops = 0;
int64 degree = 0;

/* the graph name is required, but this generally isn't user supplied */
if (PG_ARGISNULL(0))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("vertex_stats: graph name cannot be NULL")));
}

/* get the graph name */
agtv_temp = get_agtype_value("vertex_stats", AG_GET_ARG_AGTYPE_P(0),
AGTV_STRING, true);

/* we need the vertex */
if (PG_ARGISNULL(1))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("vertex_stats: vertex cannot be NULL")));
}

/* get the vertex */
agtv_vertex = get_agtype_value("vertex_stats", AG_GET_ARG_AGTYPE_P(1),
AGTV_VERTEX, true);

graph_name = pnstrdup(agtv_temp->val.string.val,
agtv_temp->val.string.len);

/* get the graph oid */
graph_oid = get_graph_oid(graph_name);

/*
* Create or retrieve the GRAPH global context for this graph. This function
* will also purge off invalidated contexts.
*/
ggctx = manage_GRAPH_global_contexts(graph_name, graph_oid);

/* free the graph name */
pfree(graph_name);

/* get the id */
agtv_temp = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_vertex, "id");
vid = agtv_temp->val.int_value;

/* get the vertex entry */
ve = get_vertex_entry(ggctx, vid);

/* zero the state */
memset(&result, 0, sizeof(agtype_in_state));

/* start the object */
result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_OBJECT,
NULL);
/* store the id */
result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
string_to_agtype_value("id"));
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);

/* store the label */
agtv_temp = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_vertex, "label");
result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
string_to_agtype_value("label"));
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);

/* set up an integer for returning values */
agtv_temp = &agtv_integer;
agtv_temp->type = AGTV_INTEGER;
agtv_temp->val.int_value = 0;

/* get and store the self_loops */
edges = get_vertex_entry_edges_self(ve);
self_loops = (edges != NULL) ? get_list_size(edges) : 0;
agtv_temp->val.int_value = self_loops;
result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
string_to_agtype_value("self_loops"));
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);

/* get and store the in_degree */
edges = get_vertex_entry_edges_in(ve);
degree = (edges != NULL) ? get_list_size(edges) : 0;
agtv_temp->val.int_value = degree + self_loops;
result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
string_to_agtype_value("in_degree"));
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);

/* get and store the out_degree */
edges = get_vertex_entry_edges_out(ve);
degree = (edges != NULL) ? get_list_size(edges) : 0;
agtv_temp->val.int_value = degree + self_loops;
result.res = push_agtype_value(&result.parse_state, WAGT_KEY,
string_to_agtype_value("out_degree"));
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE, agtv_temp);

/* close the object */
result.res = push_agtype_value(&result.parse_state, WAGT_END_OBJECT, NULL);

result.res->type = AGTV_OBJECT;

PG_RETURN_POINTER(agtype_value_to_agtype(result.res));
}

0 comments on commit b558c70

Please sign in to comment.