Copilot commented on code in PR #13879:
URL: https://github.com/apache/skywalking/pull/13879#discussion_r3285272002


##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIndexInstaller.java:
##########
@@ -806,21 +824,29 @@ private void checkMeasure(Model model, Measure measure, 
BanyanDBClient client, S
                     opt.recordOutcome("measure", 
hisMeasure.getMetadata().getName(),
                         StorageManipulationOpt.Outcome.SKIPPED_SHAPE_MISMATCH,
                         "backend shape differs from declared shape; use 
/runtime/rule/addOrUpdate to reshape");
-                    return;
+                    return false;
                 }
                 // banyanDB server can not delete or update Tags.
                 opt.recordModRevision(client.update(measure));
                 log.info("update Measure: {} from: {} to: {}", 
hisMeasure.getMetadata().getName(), hisMeasure, measure);
             }
         }
+        return true;
     }
 
     /**
      * Check if the stream exists and update (or record shape mismatch) per 
mode.
      * See {@link #checkMeasure} for the create-if-absent vs full-install 
contract,
      * including the {@link Model#isAllowBootReshape()} additive opt-in.
      */
-    private void checkStream(Model model, Stream stream, BanyanDBClient 
client, StorageManipulationOpt opt) throws BanyanDBException {
+    /**
+     * @return {@code true} when the live stream is now aligned with the 
declared shape

Review Comment:
   `checkStream(...)` also has two consecutive Javadoc blocks. This leaves a 
stray `/** ... */` comment not attached to any declaration and duplicates the 
docs. Combine them into a single Javadoc immediately above the method.



##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIndexInstaller.java:
##########
@@ -796,7 +814,7 @@ private void checkMeasure(Model model, Measure measure, 
BanyanDBClient client, S
                         opt.recordOutcome("measure", 
hisMeasure.getMetadata().getName(),
                             StorageManipulationOpt.Outcome.UPDATED,
                             "additive boot reshape: new tag / field added");

Review Comment:
   The outcome message for the additive boot reshape only mentions adding new 
tags/fields, but this PR also treats tag relocation (storage-only ↔ searchable) 
as additive. Updating the message makes operator-facing outcomes less 
misleading.
   



##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIndexInstaller.java:
##########
@@ -878,12 +905,42 @@ private boolean canBootReshape(Model model, 
StorageManipulationOpt opt) {
     }
 
     /**
-     * Purely-additive diff for a BanyanDB {@link Stream}: declared may add 
tag families or
-     * tags, but every tag-family / tag that already exists on the backend 
must be present
-     * with the same name and {@link BanyandbDatabase.TagType type}, the 
{@link BanyandbDatabase.Entity entity}
-     * column list must match exactly (reshape can't change shard / series-id 
semantics),
-     * and no tag may be dropped. Returns false for any non-additive 
divergence so the caller
-     * falls back to {@link 
StorageManipulationOpt.Outcome#SKIPPED_SHAPE_MISMATCH}.
+     * Record a parallel {@link 
StorageManipulationOpt.Outcome#SKIPPED_SHAPE_MISMATCH} for the
+     * index-rule + binding (+ TopN, for measures) resources of a stream / 
measure / trace

Review Comment:
   The Javadoc says this helper records outcomes for "index-rule + binding (+ 
TopN, for measures)", but `skipDependentReconcile` only records outcomes for 
index rules/binding. Either record a TopN outcome too, or remove the TopN 
mention to keep the comment accurate.
   



##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIndexInstaller.java:
##########
@@ -878,12 +905,42 @@ private boolean canBootReshape(Model model, 
StorageManipulationOpt opt) {
     }
 
     /**
-     * Purely-additive diff for a BanyanDB {@link Stream}: declared may add 
tag families or
-     * tags, but every tag-family / tag that already exists on the backend 
must be present
-     * with the same name and {@link BanyandbDatabase.TagType type}, the 
{@link BanyandbDatabase.Entity entity}
-     * column list must match exactly (reshape can't change shard / series-id 
semantics),
-     * and no tag may be dropped. Returns false for any non-additive 
divergence so the caller
-     * falls back to {@link 
StorageManipulationOpt.Outcome#SKIPPED_SHAPE_MISMATCH}.
+     * Record a parallel {@link 
StorageManipulationOpt.Outcome#SKIPPED_SHAPE_MISMATCH} for the
+     * index-rule + binding (+ TopN, for measures) resources of a stream / 
measure / trace
+     * whose primary {@code check*} just skipped. Calling
+     * {@code checkIndexRules} / {@code checkIndexRuleBinding} unconditionally 
after a primary
+     * skip would silently update the binding to reference the new declared 
rule list while
+     * the underlying schema still carries the old shape — operators end up 
with a binding
+     * pointing at tags / fields that don't agree with the live tag family 
layout (e.g. a tag
+     * was dropped from the declared model but kept on the backend, the 
binding loses its
+     * reference, and the orphan IndexRule becomes unqueryable).
+     *
+     * <p>Skipping the dependent reconcile keeps live state coherent: either 
everything
+     * matches the declared shape, or nothing on this resource is touched 
until the operator
+     * drops + recreates.
+     */
+    private void skipDependentReconcile(StorageManipulationOpt opt, String 
resourceType, String resourceName) {
+        log.warn("BanyanDB {} {} shape mismatch — skipping dependent IndexRule 
/ IndexRuleBinding "
+                + "reconciliation to avoid partial reshape (binding would 
point at the new tag "
+                + "list while the live tag families still carry the old 
shape).",
+            resourceType, resourceName);
+        opt.recordOutcome("indexRules", resourceName,
+            StorageManipulationOpt.Outcome.SKIPPED_SHAPE_MISMATCH,
+            resourceType + " shape mismatch; index-rule reconcile skipped");

Review Comment:
   `StorageManipulationOpt.ResourceOutcome` documents resourceType labels like 
`indexRule` / `indexRuleBinding` 
(server-core/.../StorageManipulationOpt.java:455-457), and this class 
consistently uses `indexRule` elsewhere. Recording `indexRules` here introduces 
a new label that likely won't be recognized/filtered consistently in operator 
output.
   



##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIndexInstaller.java:
##########
@@ -840,7 +866,7 @@ private void checkStream(Model model, Stream stream, 
BanyanDBClient client, Stor
                         opt.recordOutcome("stream", 
hisStream.getMetadata().getName(),
                             StorageManipulationOpt.Outcome.UPDATED,
                             "additive boot reshape: new tag added");

Review Comment:
   Same as the measure path: this outcome message says "new tag added", but an 
additive boot reshape may also be a tag relocation between families. Align the 
message with the supported behavior so the recorded outcome is accurate.
   



##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIndexInstaller.java:
##########
@@ -776,7 +785,16 @@ private long defineIndexRuleBinding(List<IndexRule> 
indexRules,
      * {@link 
org.apache.skywalking.oap.server.core.storage.model.ModelInstaller#whenCreating}
      * so only one node races on the DDL.
      */
-    private void checkMeasure(Model model, Measure measure, BanyanDBClient 
client, StorageManipulationOpt opt) throws BanyanDBException {
+    /**
+     * @return {@code true} when the live measure is now aligned with the 
declared shape
+     *         (either it already matched, or the installer successfully 
applied an update);
+     *         {@code false} when the shape diverged and the installer recorded

Review Comment:
   There are two consecutive Javadoc blocks immediately before 
`checkMeasure(...)`. The first `/** ... */` is no longer attached to any symbol 
and will be treated as a stray Javadoc comment, which can trip style checks and 
makes the method docs harder to follow. Merge the two blocks into a single 
Javadoc on the method.



##########
oap-server/server-storage-plugin/storage-banyandb-plugin/src/test/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBIT.java:
##########
@@ -608,6 +608,143 @@ public void 
testStreamNonAdditiveBootReshape_optInStillSkips() throws Exception
             "expected SKIPPED_SHAPE_MISMATCH for non-additive (type-change) 
diff even with opt-in, got " + bootOpt.getOutcomes());
     }
 
+    /**
+     * Toggling {@code storageOnly} on an existing {@code @Column} moves the 
tag from
+     * {@code storage-only} → {@code searchable} (or vice versa). Although the 
live tag
+     * family no longer contains the tag at its old position, the tag identity 
+ type are
+     * preserved, so {@link BanyanDBIndexInstaller#isPurelyAdditiveStream} (via
+     * {@code isPurelyAdditiveTagFamilies}) should accept the relocation when
+     * {@code allowBootReshape = true} and the OAP is in the init / standalone 
path. The
+     * dependent IndexRule for the now-indexed tag should also be created.
+     */
+    @Test
+    public void testStreamStorageOnlyTogglePathBootReshape() throws Exception {
+        DownSamplingConfigService downSamplingConfigService = new 
DownSamplingConfigService(Arrays.asList("minute"));
+        ModuleManager moduleManager = mock(ModuleManager.class);
+        ModuleProviderHolder moduleProviderHolder = 
mock(ModuleProviderHolder.class);
+        ModuleServiceHolder moduleServiceHolder = 
mock(ModuleServiceHolder.class);
+        
when(moduleManager.find(CoreModule.NAME)).thenReturn(moduleProviderHolder);
+        when(moduleProviderHolder.provider()).thenReturn(moduleServiceHolder);
+        
when(moduleServiceHolder.getService(DownSamplingConfigService.class)).thenReturn(downSamplingConfigService);
+
+        StorageModels models = new StorageModels();
+        Model baseModel = models.add(TestStreamStorageOnly.class, 
DefaultScopeDefine.SERVICE,
+                                     new Storage("relocStream", true, 
DownSampling.Second),
+                                     
StorageManipulationOpt.withSchemaChange());
+        BanyanDBIndexInstaller installer = new BanyanDBIndexInstaller(client, 
moduleManager, config);
+        installer.isExists(baseModel, 
StorageManipulationOpt.withSchemaChange());
+        installer.createTable(baseModel);
+
+        String groupName = MetadataRegistry.convertGroupName(
+            config.getGlobal().getNamespace(), 
BanyanDB.StreamGroup.RECORDS_LOG.getName());
+        BanyandbDatabase.Stream initial = client.client.findStream(groupName, 
"relocStream");
+        // payload starts in storage-only family
+        assertTrue(initial.getTagFamiliesList().stream()
+                .filter(f -> "storage-only".equals(f.getName()))
+                .flatMap(f -> f.getTagsList().stream())
+                .anyMatch(t -> "payload".equals(t.getName())),
+            "expected payload tag in storage-only family initially, got " + 
initial);
+
+        models.remove(TestStreamStorageOnly.class, 
StorageManipulationOpt.withSchemaChange());
+        Model reshapedModel = models.add(TestStreamStorageOnlyOff.class, 
DefaultScopeDefine.SERVICE,
+                                         new Storage("relocStream", true, 
DownSampling.Second),
+                                         
StorageManipulationOpt.withSchemaChange());
+        assertTrue(reshapedModel.isAllowBootReshape());
+
+        StorageManipulationOpt bootOpt = 
StorageManipulationOpt.schemaCreateIfAbsent();
+        new BanyanDBIndexInstaller(client, moduleManager, 
config).isExists(reshapedModel, bootOpt);
+
+        BanyandbDatabase.Stream reshaped = client.client.findStream(groupName, 
"relocStream");
+        // payload is now in searchable family
+        assertTrue(reshaped.getTagFamiliesList().stream()
+                .filter(f -> "searchable".equals(f.getName()))
+                .flatMap(f -> f.getTagsList().stream())
+                .anyMatch(t -> "payload".equals(t.getName())),
+            "expected payload tag relocated to searchable family after 
reshape, got " + reshaped);
+        assertFalse(reshaped.getTagFamiliesList().stream()
+                .filter(f -> "storage-only".equals(f.getName()))
+                .flatMap(f -> f.getTagsList().stream())
+                .anyMatch(t -> "payload".equals(t.getName())),
+            "expected payload tag no longer in storage-only family after 
reshape, got " + reshaped);
+
+        boolean updatedRecorded = bootOpt.getOutcomes().stream()
+            .anyMatch(o -> "stream".equals(o.getResourceType())
+                && "relocStream".equals(o.getResourceName())
+                && o.getStatus() == StorageManipulationOpt.Outcome.UPDATED);
+        assertTrue(updatedRecorded, "expected UPDATED outcome for storageOnly 
relocation, got " + bootOpt.getOutcomes());

Review Comment:
   This test validates the tag family relocation, but it doesn't assert that 
the dependent `IndexRule` and `IndexRuleBinding` were created/updated for the 
newly-indexed `payload` tag. Adding assertions here would directly cover the 
behavior this PR is enabling (relocation should allow index-rule reconciliation 
to proceed).
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to