Skip to content

Commit 5a865ca

Browse files
committed
simpler serialization change detection
1 parent 7ac8477 commit 5a865ca

File tree

3 files changed

+39
-27
lines changed

3 files changed

+39
-27
lines changed

SpiffWorkflow/bpmn/serializer/default/workflow.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from uuid import UUID
2121

2222
from SpiffWorkflow.bpmn.specs.mixins.subworkflow_task import SubWorkflowTask
23+
from SpiffWorkflow.util.deep_merge import DeepMerge
2324
from SpiffWorkflow.util.copyonwrite import CopyOnWriteDict
2425

2526
from ..helpers.bpmn_converter import BpmnConverter
@@ -29,16 +30,18 @@ class TaskConverter(BpmnConverter):
2930
def to_dict(self, task):
3031
# Optimize serialization by storing only local changes (delta)
3132
# instead of the full materialized data when task has a parent
32-
if isinstance(task.data, CopyOnWriteDict) and task.parent is not None:
33-
# Store only local modifications (delta from parent)
34-
task_data = task.data.get_local_data()
35-
delta_serialization = True
33+
34+
if task.parent is None:
35+
data = task.data
36+
delta = {}
3637
else:
37-
# Root task or regular dict: serialize full data
38-
task_data = task.data.materialize() if isinstance(task.data, CopyOnWriteDict) else task.data
39-
delta_serialization = False
38+
data = {}
39+
delta = {
40+
'updates': DeepMerge.get_updated_keys(task.parent.data, task.data),
41+
'deletions': DeepMerge.get_deleted_keys(task.parent.data, task.data),
42+
}
4043

41-
result = {
44+
return {
4245
'id': str(task.id),
4346
'parent': str(task._parent) if task.parent is not None else None,
4447
'children': [ str(child) for child in task._children ],
@@ -47,16 +50,12 @@ def to_dict(self, task):
4750
'task_spec': task.task_spec.name,
4851
'triggered': task.triggered,
4952
'internal_data': self.registry.convert(task.internal_data),
50-
'data': self.registry.convert(self.registry.clean(task_data)),
53+
'data': data,
54+
'delta': delta
5155
}
5256

53-
# Mark delta serialization for deserialization
54-
if delta_serialization:
55-
result['data_is_delta'] = True
56-
57-
return result
58-
5957
def from_dict(self, dct, workflow):
58+
6059
task_spec = workflow.spec.task_specs.get(dct['task_spec'])
6160
task = self.target_class(workflow, task_spec, state=dct['state'], id=UUID(dct['id']))
6261
task._parent = UUID(dct['parent']) if dct['parent'] is not None else None
@@ -65,14 +64,17 @@ def from_dict(self, dct, workflow):
6564
task.triggered = dct['triggered']
6665
task.internal_data = self.registry.restore(dct['internal_data'])
6766

68-
# Handle delta vs full data serialization
69-
restored_data = self.registry.restore(dct['data'])
70-
if dct.get('data_is_delta', False) and task.parent is not None:
71-
# Reconstruct full data from parent + local delta
72-
task.data = CopyOnWriteDict(parent=task.parent.data, **restored_data)
67+
delta = dct.get('delta')
68+
if delta:
69+
data = DeepMerge.merge({}, task.parent.data)
70+
data.update(self.registry.restore(delta.get('updates', {})))
71+
for key in delta.get('deletions', {}):
72+
if key in data:
73+
del data[key]
7374
else:
74-
# Full data (backward compatible with old serializations)
75-
task.data = restored_data
75+
data = self.registry.restore(dct['data'])
76+
77+
task.data = data
7678

7779
return task
7880

SpiffWorkflow/task.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from .util.task import TaskState, TaskFilter, TaskIterator
2727
from .util.deep_merge import DeepMerge
28-
from .util.copyonwrite import CopyOnWriteDict
28+
#from .util.copyonwrite import CopyOnWriteDict
2929
from .exceptions import WorkflowException
3030

3131
logger = logging.getLogger('spiff.task')
@@ -315,16 +315,16 @@ def _assign_new_thread_id(self, recursive=True):
315315
return self.thread_id
316316

317317
def _inherit_data(self):
318-
"""Inherits data from the parent using copy-on-write semantics."""
318+
"""Inherits data from the parent."""
319+
self.data = DeepMerge.merge(self.data, self.parent.data)
319320
# Preserve any data that was already set on this task before inheriting
320321
# (e.g., multi-instance input items set before _update is called)
321322
# But parent data takes precedence for conflicting keys (matches old behavior)
322-
existing_only = {k: v for k, v in (self.data or {}).items()
323-
if k not in self.parent.data}
323+
#existing_only = {k: v for k, v in (self.data or {}).items() if k not in self.parent.data}
324324

325325
# Use CopyOnWriteDict to share parent data until modifications are made
326326
# This avoids expensive deepcopy operations for every task
327-
self.data = CopyOnWriteDict(parent=self.parent.data, **existing_only)
327+
#self.data = CopyOnWriteDict(parent=self.parent.data, **existing_only)
328328

329329
def _set_internal_data(self, **kwargs):
330330
"""Defines the given attribute/value pairs in this task's internal data."""

SpiffWorkflow/util/deep_merge.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,13 @@ def merge_array(a, b, path=None):
7474

7575
# Trim a back to the length of b. In the end, the two arrays should match
7676
del a[len(b):]
77+
78+
@staticmethod
79+
def get_updated_keys(a, b):
80+
"""get a list of keys from b that are different from a"""
81+
return dict((key, b[key]) for key in b if key not in a or b[key] != a[key])
82+
83+
@staticmethod
84+
def get_deleted_keys(a, b):
85+
"""get a list of keys from a that do not exist in b"""
86+
return [key for key in a if key not in b]

0 commit comments

Comments
 (0)