Repository navigation
Conversation
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit 3a325f2.
The table above displays the top 10 most important findings. Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
PR Reviewer Guide 🔍(Review updated until commit 3a325f2)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 3a325f2 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 61726e3
Suggestions up to commit 4f988ec
Suggestions up to commit c349f83
Suggestions up to commit 168150e
Suggestions up to commit ebea19d
|
Codecov Report✅ All modified and coverable lines are covered by tests. ❌ Your project check has failed because the head coverage (63.66%) is below the target coverage (99.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #5839 +/- ##
============================================
+ Coverage 63.36% 63.66% +0.30%
- Complexity 8872 9022 +150
============================================
Files 949 959 +10
Lines 40322 40836 +514
Branches 4546 4635 +89
============================================
+ Hits 25549 26000 +451
- Misses 13929 13964 +35
- Partials 844 872 +28
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Persistent review updated to latest commit 168150e |
Adds CalciteOtelLogsDataStreamIT and CalciteOtelTracesDataStreamIT for opensearch-project#5806. Each rolls an OTel data stream over so that the second backing index changes, removes or adds one attributes.types field, then runs PPL through the stream name and a wildcard at pushdown on. The 21 pairs among text, keyword, byte, integer, long, date and object each run twice with the types swapped between the backing indices, so both merge winners run on one node. A 500 fails the run unless a pin ties it to an issue, and a pinned cell that stops returning 500 fails until its pin is updated. A 200 or a 4xx passes. Today 93 keys, 186 queries, in each IT are pinned to opensearch-project#5811, opensearch-project#5825, opensearch-project#5835 and opensearch-project#5836. MappingConflictFixtures holds the field types, the 19 PPL templates and the pins, so a fix that clears a 500 edits only that file. OtelDataStreamConflictTestCase holds the flow both ITs share. The ITs run in a new mappingConflictIntegTest task. Its single node disables the InternalComposite asserts, since with assertions on the node exits on a mixed-type composite key. integTest and integTestRemote exclude both ITs, and both integration steps of sql-test-and-build-workflow.yml run the task. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
168150e to
c349f83
Compare
|
Persistent review updated to latest commit c349f83 |
| Response response = run("source=" + source + " | fields " + field + " | head 1"); | ||
| return response.status() == 200 && !response.type().isEmpty() | ||
| ? response.type() | ||
| : "unread-" + response.status(); |
There was a problem hiding this comment.
A 4xx is supposed to pass, but if the winner probe gets a 4xx, the run fails. So a fix that returns 400 on conflicting types can never pass. Could a 4xx probe be allowed?
There was a problem hiding this comment.
Fixed. A winner query that returns a 4xx on a type pair no longer fails the run, so a fix that rejects conflicting types with a 400 can pass. One that returns a 5xx, or a 200 with no type, still fails, and so does a 4xx on the scenarios where one side has no field, since nothing conflicts there. If a winner-scoped entry in KNOWN_500S loses its winner that way, the run asks for that entry to be removed.
| String tag = tag(); | ||
| Set<String> serverErrors = new TreeSet<>(); | ||
| for (Cell cell : cells) { | ||
| if (cell.status() >= 500) { |
There was a problem hiding this comment.
Only 500s are checked, so a command that returns 4xx everywhere (say, from a typo) passes without testing anything. Could we assert each command gets at least one 200?
There was a problem hiding this comment.
Agreed, and it is added. The run now fails when a command never returns 200 across its 112 queries, so a broken template can no longer pass by being rejected everywhere. A 200 that never reads the field still passes, as where f > f did once the planner folded it to an empty result. I can add an _explain check to this PR that fails any command whose plan never names the field.
| } | ||
| } | ||
|
|
||
| boolean covers(String pair, String cellWinner, String command) { |
There was a problem hiding this comment.
Pins don't include the source (otel-logs-drift vs otel-logs-drift*). If a 500 happens on only one source, it can't be pinned. Could Pin take an optional source?
There was a problem hiding this comment.
Added. Known500, renamed from Pin in penghuo's review, now takes an optional Source, meaning DATA_STREAM, WILDCARD or BOTH.
penghuo
left a comment
There was a problem hiding this comment.
runDrifts over 100 lines, hard for human to read.
my 2 cents
protected void runDrifts(String name) throws IOException {
List<Scenario> scenarios = Scenario.allDrifts(TYPES);
Observations observed = observe(name, scenarios);
....
}
And many new conept in this PR, but not explain what is it, e.g. Cell, it has specific meaning in CS, you would better find a better word.
| ./gradlew --continue build -x spotlessCheck -x integTest -x tracingIntegTest -x yamlRestTest -x doctest | ||
| elif [ "${{ matrix.test-type }}" = "integration" ]; then | ||
| ./gradlew --continue integTest tracingIntegTest yamlRestTest | ||
| ./gradlew --continue integTest tracingIntegTest yamlRestTest mappingConflictIntegTest |
There was a problem hiding this comment.
Any reason mappingConflictIntegTest not part of integTest?
There was a problem hiding this comment.
Yes, because A stats ... by over a field that is keyword in one backing index and a number or date in the other, the #5811 case, trips the InternalComposite$InternalBucket.compareKey assert in OpenSearch core. The integTest node runs with assertions on, so the first such query kills the node and fails every IT after it. This task runs them on their own node with only that assert off, as production runs. It follows tracingIntegTest, which also runs on its own cluster beside integTest.
| } | ||
| } | ||
| } | ||
| System.out.println( |
There was a problem hiding this comment.
Avoid using System.out.println, it is not controlled by testLogging { events "passed", "skipped", "failed" }
There was a problem hiding this comment.
Fixed, the three System.out.println calls now go through the logger inherited from OpenSearchTestCase.
| /** Runs every drift under {@code name} and fails on any unpinned 500 or stale pin. */ | ||
| protected void runDrifts(String name) throws IOException { | ||
| JSONArray nodes = new JSONArray(executeRequest(new Request("GET", "/_cat/nodes?format=json"))); | ||
| if (nodes.length() != 1) { |
There was a problem hiding this comment.
nit: why test releated to node count?
There was a problem hiding this comment.
Fixed with a Javadoc on requireOneNode. The merged schema keeps the type of the backing index it iterates last, in an order set by the index names and a salt each JVM draws at start. Every scenario recreates the same two names, so on one node the two scenarios of a pair let each type win once. On several nodes each node has its own order and requests spread across them, so a query's winner can differ from the one its probe read.
| static final List<String> TYPES = | ||
| List.of("text", "keyword", "byte", "integer", "long", "date", "object"); |
There was a problem hiding this comment.
How to support future supported TYPES, e.g. flat_object, #5830
There was a problem hiding this comment.
A type the fixture already holds is one entry in TYPES, and the run then pairs it with every other type. flat_object is not among the 22 types the fixture holds under attributes.types, so it also needs a field and base values in both data files and a mapping in both templates, since dynamic mapping never produces flat_object.
I tried it on main with #5830. Against keyword, the numbers and date it returns only 200 or 400, and it adds 24 keys returning 500 when it stands alone or wins against text or object, all from does not support doc_value in root field on a sort or aggregation pushed to the flat_object root. I can add it in this or a follow-up PR, together with an issue for that 500.
| * fix narrows its pin to the cells that still fail, splitting the pin if they no longer form one | ||
| * set of commands over one set of pairs, and the run fails until it does. | ||
| */ | ||
| record Pin(String issue, List<String> commands, List<String> pairs, String winner) { |
There was a problem hiding this comment.
what is the meaing of Pin from OO design perspective?
There was a problem hiding this comment.
Renamed to Known500, a group of queries that return a 500 today, tied to the issue that tracks them. The list keeps its name, KNOWN_500S, which now matches the type.
| * test and {@code {K}} for a control field. | ||
| */ | ||
| record Command(String name, String template) { | ||
| String query(String source, String field, String control) { |
There was a problem hiding this comment.
what is the meaning of control?
There was a problem hiding this comment.
Removed. It only fed a second field to five templates, such as chart count() over {K} by {F}, and the run gives the same 500s without it, so those templates now read only the field under test.
| } | ||
| List<Command> commands = REPRESENTATIVE; | ||
| Set<String> names = commands.stream().map(Command::name).collect(Collectors.toSet()); | ||
| List<Cell> cells = new ArrayList<>(); |
There was a problem hiding this comment.
what is the defition of Cell from OO perpective?
There was a problem hiding this comment.
Renamed to Observation, the outcome of one query, meaning one command over one source under one winner. The checks over all of them now live on an Observations record.
| String a = TYPES.get(i); | ||
| String b = TYPES.get(j); | ||
| String pair = a + "/" + b; | ||
| cells.addAll(pass(name, pair, a, retype(a, b), commands)); |
There was a problem hiding this comment.
pass() is confusing, it just run query, but did not pass any test.
There was a problem hiding this comment.
Agreed, it is now observeScenario, called from observe(name, scenarios), which runs every scenario and returns the Observations the named checks read.
A winner probe that returns a 4xx on a type pair no longer fails the run, so a fix that rejects conflicting types with a 400 can pass. A 4xx probe on a pass where one side has no field still fails, since nothing conflicts there. The run now fails when a command never returns 200, so a template that every query rejects cannot pass while testing nothing. Pin takes an optional Source, DATA_STREAM, WILDCARD or BOTH, so a 500 that only one source returns can be pinned. The existing constructor defaults to BOTH, and the five pins are unchanged. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
Cell becomes Observation, the outcome of one query. Pin becomes Known500, a group of queries that return a 500 today, tied to the issue that tracks them. pass() becomes observe() and control() becomes stableField(). The Javadocs and failure messages drop the pin and cell wording to match. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
runDrifts now observes Scenario.allDrifts(TYPES) into an Observations record, logs the summary, and collects failures from four checks on that record. They cover unreadable winners, commands that never return 200, 500s missing from KNOWN_500S, and stale KNOWN_500S entries. A plant that fires every failure path gave a byte-identical failure message before and after the split, under the same merge read order. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
The merge order comes from the index names and a per-JVM salt, so one node keeps every scenario on one order and a/b then b/a lets each type win once. The node check message drops the undefined word swap. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
Rename missingKnown500s to unlisted500s, the per-scenario observe to observeScenario, and Scenario.drift to driftFactory. Split staleKnown500s into commandsThatNeverRun, unreachedPairs and noLonger500s. Move the drift, pair and winner definitions onto Scenario and Observation, state the pair order rule, and name the pairs an earlier merge rule decides. A plant that fires every failure path gave a byte-identical failure message before and after, under the same merge read order. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
|
Persistent review updated to latest commit 4f988ec |
Five command templates read a second keyword field, {K}, next to the
field under test. The run gives the same 500s without it, so the
templates now read only {F}, and stableField() is gone from the base
class and both ITs. Both ITs still run 2128 queries with 186 returning
500 across 93 keys, every one listed in KNOWN_500S.
Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
|
Persistent review updated to latest commit 61726e3 |
|
Thank you, @penghuo, I appreciate the review. All nine comments are addressed, with an answer on each thread.
|
Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
|
Persistent review updated to latest commit 3a325f2 |
Description
Adds PPL integration tests over OTel logs and traces data streams for #5806, scope items 1 to 4. Each test rolls a data stream over so that the second backing index changes, removes or adds one field, then runs PPL through the stream name and a wildcard. A 500 fails the run unless
KNOWN_500Slists it with its issue.OtelDataStreamConflictTestCase.javaScenariois one rollover, anObservationis the outcome of one query, andObservationsholds the checks that turn them into failuresCalciteOtelLogsDataStreamIT.javaandCalciteOtelTracesDataStreamIT.javaMappingConflictFixtures.javabuild.gradlemappingConflictIntegTesttask on its own single-node cluster, excluded fromintegTestandintegTestRemote. With assertions on, the node exits on theInternalComposite$InternalBucket.compareKeyassert that every #5811 query reaches, so this cluster turns off theInternalCompositeasserts and returns the 500 a production node returnssql-test-and-build-workflow.ymlyamlRestTest. It took about 3.5 minutes locallyHow it works. Each scenario does five steps.
attributes.types.<type>field to another type, drops it, or adds a new one, and loads 5 more documents.fields <field> | head 1.<stream>*, and records the HTTP status.The 21 pairs among
text,keyword,byte,integer,long,dateandobjecteach take two scenarios, with the types swapped between 000001 and 000002. Inside one node the merge reads the backing indices in a fixed order, which a standalone probe showed and the run does not re-check, so the two scenarios give the merge both orders a two-index stream can produce, and cover every winner production can return. When a merge rule picks the type, such astextagainstkeyword, both scenarios read that type. Each type is also removed once and added once, for 56 scenarios per signal.The run refuses a cluster with more than one node, since the scenarios rely on one fixed read order. It fails on a 500 missing from
KNOWN_500Sand on a known 500 that no longer returns 500, as the lifecycle below shows. It also fails on a command that never returns 200, since such a command tests nothing.What it checks. Only the never-500 part of #5806's acceptance, at pushdown on. A command's 200 or 4xx outside
KNOWN_500Spasses.Known 500s. Today 93 keys return 500 in each IT, from four causes, where a key is one pair, winner and command, run through both sources for 186 queries. Logs and traces return the same ones, since the drift lands on the same fields. A
Known500entry covers both sources by default, and it can nameDATA_STREAMorWILDCARDfor a 500 that only one source returns.stats count() by,dedup,top,rare,chart,timechartkeywordagainstbyte,integerorlong, and againstdatewhen the string side winssort,reverseandheadafter a sort, and a selfjoinkeywordagainstbyte,integer,longordatestats max, pushed down as a sortedtop_hitsstats maxon an objecttext/object,object/absentandabsent/objectHow a known 500 behaves in CI. One query,
stats maxoverkeyword/bytewhen the string side wins, through its life.KNOWN_500Sexpects that, so the run passes.KNOWN_500S, so that PR's run fails.A brand new 500 fails the same way as step 4. Its author fixes it, or files an issue and adds an entry to
KNOWN_500S.Testing.
./gradlew :integ-test:mappingConflictIntegTestpasses on a local single node, macOS on JDK 21. Each IT ran 2,128 queries in about 95 s. CI on the first commit passed every integration job, and the Linux JDK 25 and Windows JDK 21 logs show both ITs passing. A temporary fake entry failed withno longer returns 500, a dropped entry with500 missing from KNOWN_500S, and planted mistakes in an entry with their own messages. One plant that fires every failure message gave the same failure text before and after the code was split into scenarios and named checks. Without the-daflag the node exits atcompareKey.spotlessCheckpasses.Related Issues
Part of #5806.
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.