pjfanning opened a new pull request, #3506:
URL: https://github.com/apache/pekko/pull/3506

   ### Motivation
   `SelectChildPattern` compiled its glob to a regular expression with 
`Helpers.makePattern`
   (`Helpers.scala:46`), which quotes the literal runs but turns every `*` into 
`.*`. A chain of
   those backtracks badly. Measured on JDK 17, matching a pattern against a 
name of all `a`s that
   cannot satisfy the trailing literal:
   
   ```
   glob  5 stars (12 chars), name 30 chars ->      5 ms
   glob  8 stars (18 chars), name 30 chars ->    214 ms
   glob 10 stars (22 chars), name 30 chars ->   1098 ms
   glob 12 stars (26 chars), name 30 chars ->   3485 ms
   
   glob  8 stars (18 chars), name 36 chars ->   1204 ms
   glob 10 stars (22 chars), name 36 chars ->   9427 ms
   glob 12 stars (26 chars), name 36 chars ->  46987 ms
   ```
   
   An `ActorSelectionMessage` carries its pattern elements in the message
   (`MessageContainerSerializer.scala:83-92`), and `SelectChildPattern`'s 
constructor compiles the
   pattern, so both the compile and the match are reachable from one small 
message.
   `ActorSelection.deliverSelection` runs on the caller's thread for a local 
selection and, for a
   remote one, directly on the inbound stream thread 
(`MessageDispatcher.scala:93`, deliberately,
   "to make sure it is not stuck on busy user actor").
   
   My first thought was to cap the pattern length in the serializer. The 
numbers above say that
   would have been useless: 26 characters is already enough, and it is not a 
suspicious-looking
   pattern. The cost has to come out of the matching itself.
   
   Note that `untrusted-mode` does not cover this either — its check
   (`MessageDispatcher.scala:81-93`) runs after deserialization, so it stops 
the matching but not
   the compiling.
   
   ### Modification
   Add `org.apache.pekko.util.Glob` (`@InternalApi`), which matches the same 
grammar — `?` is
   exactly one character, `*` is any run, everything else is literal — with the 
standard
   single-backtrack-point wildcard algorithm: it remembers only the most recent 
`*` and how much
   it has consumed, so it never revisits a decision more than once per input 
position. Worst case
   is proportional to the product of the two lengths; in practice it is linear.
   
   `ActorSelection.deliverSelection` now matches through 
`SelectChildPattern.matches`.
   `SelectChildPattern.pattern` is kept for compatibility but is now `lazy`, so 
deserializing a
   selection no longer compiles a regular expression at all.
   
   Nothing else used `Helpers.makePattern`, which is public API and is left in 
place.
   
   I did not add a cap on the number of selection elements. Once matching is 
linear the remaining
   per-element cost is a small object allocation, already bounded by the frame 
size.
   
   ### Result
   The same selections match as before, in time linear in practice.
   
   ### Tests
   - `sbt "actor-tests/testOnly org.apache.pekko.util.GlobSpec"` — 6 passed. 
The important one is
     `agree with the regular expression it replaces, exhaustively over short 
inputs`: every pattern
     over `{a, b, *, ?}` up to length 4 (341 of them) against every input over 
`{a, b}` up to
     length 4, compared against `Helpers.makePattern`. All agree, so the 
grammar is unchanged.
   - `sbt "actor-tests/testOnly org.apache.pekko.actor.ActorSelectionSpec"` — 
28 passed, including a
     new end-to-end test that sends a wildcard selection with the pathological 
pattern to a parent
     with a 36 character child and asserts it returns in under 3 seconds.
   - That end-to-end test was checked to discriminate: reverted to the regex 
matching it fails with
     `42561317850 was not less than 3000000000` — 42.5 seconds.
   - `sbt "remote/testOnly 
org.apache.pekko.remote.serialization.MessageContainerSerializerSpec"` — 3 
passed
   - `sbt "actor/mimaReportBinaryIssues"` — no issues
   - `sbt "actor/scalafmtCheckAll" "actor-tests/scalafmtCheckAll" 
headerCreateAll` — clean
   
   ### References
   None. `Glob.scala` is new code and carries the standard ASF header.
   


-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to