Mark Payne created NIP-50:
-----------------------------
Summary: Partial Component State Updates
Key: NIP-50
URL: https://issues.apache.org/jira/browse/NIP-50
Project: NiFi Improvement Proposal
Issue Type: Improvement
Reporter: Mark Payne
Assignee: Mark Payne
h2. Motivation
{{setState}} and {{replace}} replace the entire state map. To change one key, a
component must get the map, copy it, change the key, and write the whole map
back. That is more code than the change needs, and two writers doing it at the
same time can drop each other's keys.
Many components store independent keys in one map (one per table, partition, or
cursor) and often change only a few of them. A method that accepts just those
keys is simpler to use. It also lets a state store persist only what changed,
which can be cheaper when the map is large.
h2. Proposed Solution
Add one method to both {{ProcessSession}} and {{StateManager}}:
{code:java}
void patchState(Map<String, String> updatedState, Scope scope) throws
IOException;
{code}
The map is a patch, not a full replacement:
* Present keys with a non-null value are created or overwritten.
* Present keys with a {{null}} value are removed. Removing a key that is not
there is a no-op. An empty string is stored; it does not remove the key.
* Keys that are not in the map are left as they are.
If no state is stored yet ({{StateMap}} version {{-1}}), the call matches
{{setState}} after {{null}} values are dropped.
A {{null}} map is not allowed. An empty map is a no-op: no write and no version
change.
{{ProcessSession.patchState}} follows the same commit rules as {{setState}}:
the patch is cached on the session, {{getState}} in that session sees it, and
it is published on commit only if it is still the latest remote state. Rollback
discards it. A second {{patchState}} in the same session applies on top of the
first cached result.
A default implementation is provided on both interfaces. Existing
{{StateManager}} and {{ProcessSession}} implementations stay source-compatible.
The default gets the current state, copies it into a new {{HashMap}}, applies
the patch, and calls {{replace}} until that succeeds:
{code}
StateMap current;
do {
current = getState(scope);
Map<String, String> updated = new HashMap<>(current.toMap());
for (each key/value in updatedState) {
if (value is null) {
updated.remove(key);
} else {
updated.put(key, value);
}
}
} while (!replace(current, updated, scope));
{code}
On {{ProcessSession}}, {{replace}} is {{replaceState}}. {{getState}} and
{{replaceState}} already see session-cached state, so the same loop honors
commit and rollback.
h2. NiFi API Changes
New default methods on {{org.apache.nifi.processor.ProcessSession}} and
{{org.apache.nifi.components.state.StateManager}}, using the loop above.
{{StateTooLargeException}} still applies to the map after the patch is applied.
Backward compatible: additive default methods only.
h2. Voting Period
This NIP is proposed with a 24-hour lazy consensus voting period.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)