[
https://issues.apache.org/jira/browse/IGNITE-28520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Anton Vinogradov updated IGNITE-28520:
--------------------------------------
Description: (was: h2. Summary
The CacheObject pre-marshal pass already runs on the user thread —
{{GridCacheIoManager.onSend}} has been calling {{msg.prepareMarshal(cctx)}} on
the sender thread since 2014 (commit by Anton Irinev, before {{ignite-63}}).
This ticket is *not* about moving work off the NIO worker; it is about
replacing the per-class hand-written CO traversal with a generated,
auto-invoked equivalent.
Today every {{GridCacheMessage}} subclass that carries {{CacheObject}} fields
has a hand-written {{prepareMarshal(GridCacheSharedContext)}} override that
walks those fields and calls {{co.prepareMarshal(ctx)}} on each. Adding a new
{{@Order CO}} field to such a class requires a matching update of the override.
Forgetting that update silently moves that field's {{Marshaller.marshal}} call
onto the NIO writer (it runs lazily inside {{writeTo}} when {{valBytes ==
null}}), which is the regression mode this ticket exists to prevent.
After this ticket lands, the same traversal is generated by APT and
auto-invoked from the framework — so coverage is structural rather than
maintained by hand.
h2. What changes
* {{MessageSerializer}} interface gets a new {{default void
prepareMarshalCacheObjects(M msg, CacheObjectValueContext ctx)}} (no-op
default).
* {{MessageSerializerGenerator}} (APT) emits this method on every concrete
serializer with at least one traversable {{@Order}} field; recurses into nested
concrete {{Message}} types via per-class {{*_SER}} static fields. Recursion is
blocked only for {{GridCacheIdMessage}} subclasses (top-level routable
messages) — helper messages with {{@Order int cacheId}} ({{GridCacheReturn}},
{{IgniteTxKey}}, {{IgniteTxEntry}}, {{GridCacheEntryInfo}}, {{UpdateErrors}})
are recursable.
* {{GridCacheIoManager.onSend}} adds
{{prepareMarshalGeneratedCacheObjects(msg)}} after the existing
{{msg.prepareMarshal(cctx)}}. Helper is gated on {{msg instanceof
GridCacheIdMessage}} and resolves {{cacheObjectContext}} from
{{((GridCacheIdMessage)msg).cacheId()}}. Both calls run on the user thread,
both are idempotent for any given {{CacheObject}} ({{if (valBytes == null)}}).
* Hand-written {{prepareMarshal(GridCacheSharedContext)}} overrides are removed
from {{GridCacheMessage}} subclasses where the body was a pure {{@Order}}-CO
traversal that the generator now performs:
** {{GridCacheTtlUpdateRequest}}, {{GridDhtForceKeysRequest}},
{{GridNearUnlockRequest}}, {{GridDhtUnlockRequest}},
{{GridDistributedLockRequest}}, {{GridDistributedLockResponse}},
{{GridDhtAtomicUpdateResponse}}, {{GridDhtAtomicNearResponse}},
{{GridNearGetRequest}}.
** Also dropped: {{ret.prepareMarshal(cctx)}} call inside
{{GridNearAtomicUpdateResponse.prepareMarshal}} (covered by nested-message
recursion in the generated serializer).
* Generator hygiene aligned with Ignite coding guidelines: {{private static
final}} modifier order, no trailing space after {{/**}} in class javadoc, no
trailing newline at EOF, empty line between top-level {{if}}-blocks in
{{prepareMarshalCacheObjects}}, empty line between map keys/values traversal
blocks.
h2. Why it is worth doing
* *Structural coverage.* New {{@Order CO}} fields are picked up automatically;
no risk of a missed override.
* *Less boilerplate.* Phase 2 cleanup deletes ~9 hand-written methods that were
doing the same loop the generator now writes.
* *One source of truth* for "which fields participate in pre-marshal traversal"
— the {{@Order}} annotations.
* *No behavioural change* for messages whose hand-written override stays — the
auto-invoke is a no-op on already-marshalled {{CacheObject}}s thanks to the
{{valBytes == null}} guard inside {{CacheObjectImpl.prepareMarshal}}.
h2. Out of scope
* Receive-side counterpart ({{finishUnmarshalCacheObjects}}) — split into a
separate ticket (IGNITE-YYYYY).
* Extending the auto-invoke to {{GridCacheMessage}} subclasses that are not
{{GridCacheIdMessage}} (e.g. {{GridDhtTxFinishResponse}}), or to nested
messages whose embedded {{cacheId}} differs from the outer's (e.g.
{{GridNearTxPrepareResponse}}'s {{retVal.cacheId()}}) — split into a separate
ticket (IGNITE-ZZZZZ). Until that lands, {{GridCacheReturn}} keeps its {{static
SER}} field and {{prepareMarshal(GridCacheContext)}} bridge for the two
responses that fall outside the auto-invoke gate.
h2. Acceptance criteria
* {{GridCacheIoManager.onSend}} calls
{{prepareMarshalGeneratedCacheObjects(msg)}} for every {{GridCacheIdMessage}}
subclass with a non-{{null}} {{cacheObjectContext}}.
* {{MessageProcessorTest}} green; fixture serializers byte-identical with
generator output.
* Targeted suites green: {{GridCacheReturnValueTransferSelfTest}},
{{IgniteCacheEntryProcessorCallTest}},
{{GridCachePartitionedTxMultiThreadedSelfTest}},
{{GridCacheRebalancingSyncSelfTest}} (force-keys path),
{{GridCachePutAllFailoverSelfTest}} (atomic update / unlock path),
{{IgniteCacheQueryMultiThreadedSelfTest}} (query/eviction path).
* Possible-blockers list from the TC bot for the PR contains no real
regressions (flakes only).)
> Auto-generate prepareMarshalCacheObjects to replace hand-written CO traversal
> in GridCacheMessage subclasses
> ------------------------------------------------------------------------------------------------------------
>
> Key: IGNITE-28520
> URL: https://issues.apache.org/jira/browse/IGNITE-28520
> Project: Ignite
> Issue Type: Task
> Reporter: Alex Abashev
> Assignee: Anton Vinogradov
> Priority: Minor
> Labels: IEP-132, ise
> Fix For: 2.19
>
> Time Spent: 19h 10m
> Remaining Estimate: 0h
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)