Skip to content

Conversation

Yang-33
Copy link
Contributor

@Yang-33 Yang-33 commented Jul 23, 2025

As far as I know, inline comments are often necessary for CONFIG_IGNORE_KEYS, CONFIG_PARTITION_CONCURRENCY, and CONFIG_MAX_PENDING_RECORDS.

Using YAML allows us to add inline comment in central dogma decaton property file.
Decaton's internal parser is Jackson, which seems to partially support YAML. By leveraging this, YAML can be supported with relatively few changes.

Of course, it’s not complete. While YAML tags, anchors, and aliases cannot be used, we can include comments in the configuration file. Practically speaking, this seems to be sufficient...

If maintainers don't want to use YAML with parital support, there are some Jackson options that support JSON5 to support comment in json file.

If it's acceptable for decaton maintainers that partial yaml support, major version up is necessary as a breaking change to accommodate users (though I don't think they exist) who might have a YAML file extension but use JSON.

Resolve #173

@ocadaruma
Copy link
Member

Hi, thank you for the patch and sorry for the late review.

Could you resolve the conflict, for the ease of review?

@Yang-33 Yang-33 force-pushed the support-yaml-partially branch from 0c972cf to 37bb1f3 Compare August 4, 2025 13:34
@Yang-33
Copy link
Contributor Author

Yang-33 commented Aug 4, 2025

rebased!

Copy link
Member

@ocadaruma ocadaruma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left one early question, but overall strategy looks good.

Please go ahead and complete the implementation. Thanks for the patch!

}

rootWatcher.watch(node -> {
node.fields().forEachRemaining(entry -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In contrast to current approach, new code sets the watch against entire file and iterates through all properties in the file.
Do you have any thoughts if it will cause some downside comparing to current approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's almost no difference in practice!

The current decaton implementation uses Central Dogma’s MappingWatcher for each key/property, which attaches a child watcher to a root watcher.
When the root watcher's content (=JSON) changes, each child watcher is triggered independently.
All it does is extract the value for a specific key from the root JSON content and notify its listener only if the value has changed.

In this PR, it delegats that logic to DynamicProperty#checkingSet, which also compares the previous and new values and does not notify listeners unless there’s an actual change.

  • public T set(T value) {
    setLock.lock();
    try {
    T currentValue = this.value;
    if (currentValue == null && value == null || currentValue != null && currentValue.equals(value)) {
    // No need to update.
    return null;
    }
    validate(value);
    this.value = value;
    logger.debug("Property {} has been updated ({} => {})", name(), currentValue, value);
    notifyListeners(currentValue, value);
    return currentValue;
    } finally {
    setLock.unlock();
    }
    }
    /**
    * Update the value of this property, taking untyped object as an argument.
    *
    * This is a slightly different version of {@link #set}.
    * Before calling {@link #set}, this method check if the passed value's runtime class is matching to the
    * type configured as {@link PropertyDefinition#runtimeType()}.
    * @param value new value to set to this property.
    * @return old value which was set to this property. null if there was no update because the new value was
    * equivalent to the current value.
    *
    * @throws IllegalArgumentException when invalid value passed.
    */
    public T checkingSet(Object value) {
    Class<?> runtimeType = definition().runtimeType();
    if (value != null && runtimeType != null && !runtimeType.isInstance(value)) {
    throw new IllegalArgumentException(String.format(
    "type %s is not applicable for property %s of type %s",
    value.getClass().getCanonicalName(), name(), runtimeType.getCanonicalName()));
    }
    return set(safeCast(value));
    }

As for the listeners, the number of change notifications won’t differ. The only difference is which layer is responsible for delivering them (Central Dogma vs Decaton).

There might be some difference in terms of parallelism for notifying new value.
Decaton has about 15 properties, and with the current implementation, Central Dogma watchers can run in parallel (as long as the Central Dogma executor allows it).
This PR updates are currently handled sequentially, but considering that the keys and values are very small, the impact is likely negligible.

  • CONFIG_IGNORE_KEYS,
    CONFIG_PROCESSING_RATE,
    CONFIG_PARTITION_CONCURRENCY,
    CONFIG_MAX_PENDING_RECORDS,
    CONFIG_COMMIT_INTERVAL_MS,
    CONFIG_GROUP_REBALANCE_TIMEOUT_MS,
    CONFIG_SHUTDOWN_TIMEOUT_MS,
    CONFIG_LOGGING_MDC_ENABLED,
    CONFIG_BIND_CLIENT_METRICS,
    CONFIG_DEFERRED_COMPLETE_TIMEOUT_MS,
    CONFIG_PROCESSOR_THREADS_TERMINATION_TIMEOUT_MS,
    CONFIG_PER_KEY_QUOTA_PROCESSING_RATE,
    CONFIG_RETRY_TASK_IN_LEGACY_FORMAT,
    CONFIG_LEGACY_PARSE_FALLBACK_ENABLED));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, we might want to avoid using node.fields() in this change, and instead explicitly check properties based on ProcessorProperties#PROPERTY_DEFINITIONS.
There’s no benefit in processing keys that Decaton doesn’t care about. Let me modify this point, though it's not main part for your question...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the detailed explanation including the implementation of CD!

I see, then the strategy looks good.

@Yang-33 Yang-33 force-pushed the support-yaml-partially branch from 1f19274 to 3db6826 Compare September 7, 2025 06:22
@Yang-33 Yang-33 force-pushed the support-yaml-partially branch from 3db6826 to 3ec1618 Compare September 7, 2025 06:25
@Yang-33 Yang-33 marked this pull request as ready for review September 7, 2025 06:38
@Yang-33
Copy link
Contributor Author

Yang-33 commented Sep 7, 2025

Thank you. I've fixed failed tests while keeping the overall strategy. Please review when you have time 🙏

@Yang-33 Yang-33 requested a review from ocadaruma September 7, 2025 06:40
@Yang-33 Yang-33 changed the title Support yaml partially(WIP) Support yaml partially Sep 14, 2025
@Yang-33 Yang-33 force-pushed the support-yaml-partially branch from 98fb0a9 to 0c1da8a Compare September 14, 2025 15:31
Copy link
Member

@ocadaruma ocadaruma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late review.
Left only one minor comment. overall LGTM!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class doesn't need to be exposed to users, so let's move to internal package (refs: https://github.com/line/decaton/blob/master/VERSIONING.md#public-apis)

JsonFormat, YamlFormat as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide CentralDogmaPropertySupplier that can read YAML
2 participants