Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filterx: perf improvement and fix race condition in readonly FilterXJson::to_json_literal() #258

Merged
merged 2 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/filterx/filterx-object.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct _FilterXObject
*/
guint modified_in_place:1, readonly:1, weak_referenced:1;
FilterXType *type;
void (*make_readonly)(FilterXObject *self);
};

FilterXObject *filterx_object_getattr_string(FilterXObject *self, const gchar *attr_name);
Expand Down Expand Up @@ -116,6 +117,9 @@ filterx_object_is_type(FilterXObject *object, FilterXType *type)
static inline void
filterx_object_make_readonly(FilterXObject *self)
{
if (self->make_readonly)
self->make_readonly(self);

self->readonly = TRUE;
}

Expand Down
38 changes: 35 additions & 3 deletions lib/filterx/object-json-array.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct FilterXJsonArray_
FilterXList super;
FilterXWeakRef root_container;
struct json_object *jso;

GMutex lock;
const gchar *cached_ro_literal;
};

static gboolean
Expand All @@ -49,12 +52,21 @@ _truthy(FilterXObject *s)
return TRUE;
}

static inline const gchar *
_json_string(FilterXJsonArray *self)
{
if (self->super.super.readonly)
return g_atomic_pointer_get(&self->cached_ro_literal);

return json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
}

static gboolean
_marshal_to_json_literal_append(FilterXJsonArray *self, GString *repr, LogMessageValueType *t)
{
*t = LM_VT_JSON;

const gchar *json_repr = json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
const gchar *json_repr = _json_string(self);
g_string_append(repr, json_repr);

return TRUE;
Expand Down Expand Up @@ -86,7 +98,7 @@ _repr(FilterXObject *s, GString *repr)
{
FilterXJsonArray *self = (FilterXJsonArray *) s;

const gchar *json_repr = json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
const gchar *json_repr = _json_string(self);
g_string_append(repr, json_repr);

return TRUE;
Expand Down Expand Up @@ -231,13 +243,29 @@ _unset_index(FilterXList *s, guint64 index)
return TRUE;
}

static void
_make_readonly(FilterXObject *s)
{
FilterXJsonArray *self = (FilterXJsonArray *) s;

if (g_atomic_pointer_get(&self->cached_ro_literal))
return;

/* json_object_to_json_string_ext() writes/caches into jso, so it's not thread safe */
g_mutex_lock(&self->lock);
if (!g_atomic_pointer_get(&self->cached_ro_literal))
g_atomic_pointer_set(&self->cached_ro_literal, json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN));
g_mutex_unlock(&self->lock);
}

/* NOTE: consumes root ref */
FilterXObject *
filterx_json_array_new_sub(struct json_object *jso, FilterXObject *root)
{
FilterXJsonArray *self = g_new0(FilterXJsonArray, 1);
filterx_list_init_instance(&self->super, &FILTERX_TYPE_NAME(json_array));

self->super.super.make_readonly = _make_readonly;
self->super.get_subscript = _get_subscript;
self->super.set_subscript = _set_subscript;
self->super.append = _append;
Expand All @@ -248,6 +276,8 @@ filterx_json_array_new_sub(struct json_object *jso, FilterXObject *root)
filterx_object_unref(root);
self->jso = jso;

g_mutex_init(&self->lock);

return &self->super.super;
}

Expand All @@ -258,6 +288,8 @@ _free(FilterXObject *s)

json_object_put(self->jso);
filterx_weakref_clear(&self->root_container);

g_mutex_clear(&self->lock);
}

FilterXObject *
Expand Down Expand Up @@ -330,7 +362,7 @@ filterx_json_array_to_json_literal(FilterXObject *s)

if (!filterx_object_is_type(s, &FILTERX_TYPE_NAME(json_array)))
return NULL;
return json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
return _json_string(self);
}

/* NOTE: Consider using filterx_object_extract_json_array() to also support message_value. */
Expand Down
40 changes: 37 additions & 3 deletions lib/filterx/object-json-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
* COPYING for details.
*
*/

#include "filterx/object-json-internal.h"
#include "filterx/object-extractor.h"
#include "filterx/object-null.h"
#include "filterx/object-primitive.h"
#include "filterx/object-string.h"
#include "filterx/filterx-weakrefs.h"
#include "filterx/object-dict-interface.h"
#include "syslog-ng.h"
#include "str-utils.h"
#include "logmsg/type-hinting.h"

Expand All @@ -35,6 +37,9 @@ struct FilterXJsonObject_
FilterXDict super;
FilterXWeakRef root_container;
struct json_object *jso;

GMutex lock;
const gchar *cached_ro_literal;
};

static gboolean
Expand All @@ -43,14 +48,23 @@ _truthy(FilterXObject *s)
return TRUE;
}

static inline const gchar *
_json_string(FilterXJsonObject *self)
{
if (self->super.super.readonly)
return g_atomic_pointer_get(&self->cached_ro_literal);

return json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
}

static gboolean
_marshal(FilterXObject *s, GString *repr, LogMessageValueType *t)
{
FilterXJsonObject *self = (FilterXJsonObject *) s;

*t = LM_VT_JSON;

const gchar *json_repr = json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
const gchar *json_repr = _json_string(self);
g_string_append(repr, json_repr);

return TRUE;
Expand All @@ -61,7 +75,7 @@ _repr(FilterXObject *s, GString *repr)
{
FilterXJsonObject *self = (FilterXJsonObject *) s;

const gchar *json_repr = json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
const gchar *json_repr = _json_string(self);
g_string_append(repr, json_repr);

return TRUE;
Expand Down Expand Up @@ -209,13 +223,29 @@ _iter(FilterXDict *s, FilterXDictIterFunc func, gpointer user_data)
return TRUE;
}

static void
_make_readonly(FilterXObject *s)
{
FilterXJsonObject *self = (FilterXJsonObject *) s;

if (g_atomic_pointer_get(&self->cached_ro_literal))
return;

/* json_object_to_json_string_ext() writes/caches into jso, so it's not thread safe */
g_mutex_lock(&self->lock);
if (!g_atomic_pointer_get(&self->cached_ro_literal))
g_atomic_pointer_set(&self->cached_ro_literal, json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN));
g_mutex_unlock(&self->lock);
}

/* NOTE: consumes root ref */
FilterXObject *
filterx_json_object_new_sub(struct json_object *jso, FilterXObject *root)
{
FilterXJsonObject *self = g_new0(FilterXJsonObject, 1);
filterx_dict_init_instance(&self->super, &FILTERX_TYPE_NAME(json_object));

self->super.super.make_readonly = _make_readonly;
self->super.get_subscript = _get_subscript;
self->super.set_subscript = _set_subscript;
self->super.unset_key = _unset_key;
Expand All @@ -226,6 +256,8 @@ filterx_json_object_new_sub(struct json_object *jso, FilterXObject *root)
filterx_object_unref(root);
self->jso = jso;

g_mutex_init(&self->lock);

return &self->super.super;
}

Expand All @@ -236,6 +268,8 @@ _free(FilterXObject *s)

json_object_put(self->jso);
filterx_weakref_clear(&self->root_container);

g_mutex_clear(&self->lock);
}

FilterXObject *
Expand All @@ -261,7 +295,7 @@ filterx_json_object_to_json_literal(FilterXObject *s)

if (!filterx_object_is_type(s, &FILTERX_TYPE_NAME(json_object)))
return NULL;
return json_object_to_json_string_ext(self->jso, JSON_C_TO_STRING_PLAIN);
return _json_string(self);
}

/* NOTE: Consider using filterx_object_extract_json_object() to also support message_value. */
Expand Down
2 changes: 1 addition & 1 deletion modules/grpc/otel/filterx/otel-field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ class OtelSeverityNumberEnumConverter : public ProtobufField
if (filterx_object_is_type(object, &FILTERX_TYPE_NAME(integer)))
{
int64_t value;
filterx_integer_unwrap(object, &value);
g_assert(filterx_integer_unwrap(object, &value));
if (!SeverityNumber_IsValid((int) value))
{
msg_error("otel-field: Failed to set severity_number",
Expand Down
19 changes: 7 additions & 12 deletions modules/json/filterx-format-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ _append_literal(const gchar *value, gsize len, GString *result)
return TRUE;
}

static gboolean
_format_and_append_json(struct json_object *value, GString *result)
{
_append_comma_if_needed(result);
g_string_append(result, json_object_to_json_string_ext(value, JSON_C_TO_STRING_PLAIN));
return TRUE;
}

static gboolean
_format_and_append_null(GString *result)
{
Expand Down Expand Up @@ -238,10 +230,13 @@ _format_and_append_value(FilterXObject *value, GString *result)
return _append_literal(str, len, result);
}

struct json_object *js;
if (filterx_object_extract_json_array(value, &js) ||
filterx_object_extract_json_object(value, &js))
return _format_and_append_json(js, result);
const gchar *json_literal = filterx_json_to_json_literal(value);
if (json_literal)
{
_append_comma_if_needed(result);
g_string_append(result, json_literal);
return TRUE;
}

if (filterx_object_extract_null(value))
return _format_and_append_null(result);
Expand Down
Loading