[
https://issues.apache.org/jira/browse/IGNITE-28966?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Aleksandr Chesnokov reassigned IGNITE-28966:
--------------------------------------------
Assignee: Aleksandr Chesnokov
> Protected Classes CI check: classes in the org.apache.ignite.internal package
> are never detected
> ------------------------------------------------------------------------------------------------
>
> Key: IGNITE-28966
> URL: https://issues.apache.org/jira/browse/IGNITE-28966
> Project: Ignite
> Issue Type: Task
> Reporter: Anton Vinogradov
> Assignee: Aleksandr Chesnokov
> Priority: Major
> Labels: IEP-132, ise
>
> The {{@Order}} annotation sets the number of a field inside a network
> message. Nodes of different versions read messages by these numbers, so a
> change here can break a rolling upgrade. The {{Protected Classes}} workflow
> ({{.github/workflows/check-protected-classes.yml}}) exists to warn a reviewer
> about such changes.
> The workflow decides that a changed file is protected by searching the whole
> file text for one string:
> {code:js}
> const ANNOTATION = 'org.apache.ignite.internal.Order';
> ...
> return content.includes(ANNOTATION);
> {code}
> That string is the full class name of the annotation. A class that lives in
> the {{org.apache.ignite.internal}} package uses {{@Order}} without importing
> it, so the string never appears in the file and the file is never reported.
> When nothing is found the script stops at {{if (hits.length === 0) return;}},
> so the pull request gets no comment, no {{compatibility}} label and no
> {{Rolling upgrade compatibility}} check. Note that the job {{Rolling Upgrade
> check}} is green in both cases: it is green even when the check does find
> something. The real signal for a reviewer is the comment, the label and the
> separate check. When detection fails none of them appear, so "nothing was
> found" looks exactly like "nothing was checked". This is the case the job
> must catch.
> h3. What the check does not see
> 13 message classes, all in
> {{modules/core/src/main/java/org/apache/ignite/internal/}}: {{ExchangeInfo}},
> {{GridJobCancelRequest}}, {{GridJobExecuteRequest}},
> {{GridJobExecuteResponse}}, {{GridJobSiblingsRequest}},
> {{GridJobSiblingsResponse}}, {{GridTaskCancelRequest}},
> {{GridTaskSessionRequest}}, {{GridTopicMessage}},
> {{IgniteDiagnosticRequest}}, {{IgniteDiagnosticResponse}}, {{TxEntriesInfo}},
> {{TxInfo}}.
> h3. What the check reports by mistake
> The same search fails the other way too, because it looks at the whole file
> text and not at the import line. 5 files name the class in javadoc, in a
> string constant, or import it to read it by reflection. None of them has a
> field with {{@Order(}}, and all of them are reported today:
> *
> {{modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/Message.java}}
> (javadoc)
> *
> {{modules/codegen/src/main/java/org/apache/ignite/internal/CustomMapper.java}}
> (javadoc)
> *
> {{modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java}}
> ({{@SupportedAnnotationTypes}})
> *
> {{modules/codegen/src/main/java/org/apache/ignite/internal/idto/IDTOSerializerGenerator.java}}
> ({{el.getAnnotation(Order.class)}})
> *
> {{modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java}}
> ({{jarForClass(Order.class)}})
> h3. How to reproduce
> The workflow runs on {{pull_request_target}}. GitHub always takes the
> workflow file from the base branch, not from the pull request. So a pull
> request against apache/ignite cannot test its own change to this file: you
> will still see the old behaviour. Test the change in your own fork. Push it
> to the fork {{master}}, then open a pull request inside the fork that changes
> an {{@Order}} number in
> {{modules/core/src/main/java/org/apache/ignite/internal/GridJobExecuteRequest.java}}.
> Live example: [#13445|https://github.com/apache/ignite/pull/13445]
> (IGNITE-28270) moves the {{@Order}} numbers of 12 fields in
> {{GridJobExecuteRequest}} from 13..24 to 14..25. The job {{Rolling Upgrade
> check}} passes in 5 seconds, and the pull request has no comment and no label.
> h3. Fix
> A file is protected when both rules hold:
> # the file text contains {{@Order(}};
> # the file has the line {{import org.apache.ignite.internal.Order;}} or the
> line {{package org.apache.ignite.internal;}}.
> Rule 1 alone is not enough. {{org.apache.ignite.internal.systemview.Order}}
> is a different annotation, imported by 52 system view classes. They are not
> messages and must stay silent. They are silent today as well, because
> {{org.apache.ignite.internal.Order}} is not a substring of
> {{org.apache.ignite.internal.systemview.Order}}.
> Rule 2 alone is not enough: that is today's bug.
> No file in the {{org.apache.ignite.internal}} package imports the system view
> {{Order}}, so the two rules together are safe. Two details carry weight:
> * The package test must be an exact match on {{package
> org.apache.ignite.internal;}}. A prefix match brings back the very classes
> the rule must skip: {{StatisticsColumnConfigurationView}},
> {{StatisticsColumnGlobalDataView}}, {{StatisticsColumnLocalDataView}} and
> {{StatisticsColumnPartitionDataView}} sit in
> {{org.apache.ignite.internal.processors.query.stat.view}} and use the system
> view annotation.
> * The open bracket in {{@Order(}} is required. {{Order.java}},
> {{MessageMarshallerGenerator.java}} and {{MessageDeploymentGenerator.java}}
> sit in the same package and write {{@Order}} in javadoc only, never
> {{@Order(}}.
> New noise: {{modules/core/src/test/resources/codegen/}} holds 72 {{.java}}
> test data files, and 41 of them match the new rule. The workflow filters on
> {{path.endsWith('.java')}} only, nothing excludes {{src/test/resources}}, so
> the new rule will start reporting them. This breaks nothing, but an explicit
> path exclusion is better.
> Keep the safety rule of this job: it runs on {{pull_request_target}} with a
> write token, so the pull request files must stay data read through the API.
> Do not add {{actions/checkout}} of the pull request head, and do not run any
> pull request code.
> h3. Second defect: an old warning is never cleared
> When nothing is found the script returns before the code that deletes an
> earlier comment, so a pull request that stopped touching protected classes
> keeps a stale warning comment.
> The {{compatibility}} label is worse. The script only calls {{addLabels}} and
> never calls {{removeLabel}}, on any path, so the label is never cleared at
> all. That part has to be written from scratch, moving the early return is not
> enough.
> The same block also deletes and re-creates the comment instead of updating
> it, so every push to a pull request that keeps touching protected classes
> sends a new notification to everyone subscribed. {{issues.updateComment}} on
> the existing comment fixes that and the stale comment case at once.
> Expected: when nothing is found, delete the old comment and remove the
> {{compatibility}} label.
> h3. Third defect: a modified file is read only at the base revision
> {code:js}
> else if (f.status === 'removed' || f.status === 'modified' || f.status ===
> 'changed')
> revisions.push([f.filename, baseSha]);
> {code}
> A pull request that *adds* {{@Order}} to an existing class, that is, moves
> the class onto codegen serialization, is never reported, because the base
> revision has no annotation yet. Unlike the package blindness above, this hole
> also hides classes outside {{org.apache.ignite.internal}}. A modified file
> should be read at both revisions, the way {{renamed}} already is.
> h3. Open question
> The {{Rolling upgrade compatibility}} check is created only when something is
> found. So "checked and clean" looks the same as "the check saw nothing", and
> the next blind spot will look fine again. Consider creating the check on
> every run: {{success}} when nothing is found, {{neutral}} when something is
> found. If this is out of scope, please say so here.
> h3. Acceptance criteria
> * On the current master the new rule matches 566 files: 525 files plus 41
> test data files under {{modules/core/src/test/resources/codegen/}}. The old
> rule matches 518. The difference is +53 (13 message classes and 40 test data
> files) and -5 (the false positives above).
> * All 13 classes listed above are matched.
> * The 5 files that only name the class in text are not matched.
> * The 52 system view classes stay unmatched.
> * A pull request that changes an {{@Order}} number in
> {{GridJobExecuteRequest}} gets the comment, the {{compatibility}} label and
> the {{Rolling upgrade compatibility}} check.
> * A pull request that adds {{@Order}} to an existing class is reported too.
> * A pull request that stopped touching protected classes loses the old
> comment and the {{compatibility}} label.
> * The workflow still does not check out and does not run the pull request
> code.
> h3. History and related work
> The script was rewritten from shell and {{grep}} to {{actions/github-script}}
> in IGNITE-28913. The {{org.apache.ignite.internal.Order}} literal goes back
> to the first version in IGNITE-28018 and survived IGNITE-28822 and
> IGNITE-28821 unchanged.
> IGNITE-28716 is Resolved as Implemented, but its pull request
> [#13171|https://github.com/apache/ignite/pull/13171] is still open (created
> 2026-05-22, never merged), and none of the files it lists are in master:
> {{scripts/check-protected-classes.sh}},
> {{tests/check-protected-classes.bats}},
> {{.github/workflows/test-workflow-scripts.yml}}, {{run-local-check.sh}}. That
> work moves the same detection into a shell script with BATS tests and a local
> runner, which is the local check this task needs. But it keeps the same
> {{org.apache.ignite.internal.Order}} literal, so it does not fix this defect,
> and it is written against the shape of the workflow before IGNITE-28822 and
> IGNITE-28913. Before you start, ask on the dev list whether #13171 will be
> merged. If yes, add the new rule and its test there. If no, fix the inline
> script in {{check-protected-classes.yml}}.
> IGNITE-25489 asks for a wider check over all {{Message}} subclasses. This
> ticket only fixes the detection rule of the existing check.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)