codeconsole opened a new pull request, #16732:
URL: https://github.com/apache/grails-core/pull/16732

   ## What
   
   The two configuration lookup changes that #16130 left out of 8.1.x because 
they change the answer for existing keys, as asked for in its review, plus the 
paths that have to agree with them. Builds on #16130: this branch merges it, so 
until #16130 is merged and 8.1.x is merged up the diff shows its commits too; 
the commits of this PR start at the cherry-pick.
   
   **A dotted key that runs past a value that is not a map is absent.** In 
Grails 8, `getProperty('server.url.port')` with `server.url: http://localhost` 
returned the URL, `getProperty('grails.gorm.default.constraints.nullable')` 
with a closure at `grails.gorm.default.constraints` returned the closure, and 
`getProperty('a.b.0')` with a list at `a.b` returned the list; 
`containsProperty` of each was `true`. Now each is `null`, the default is 
returned when one is given, and `containsProperty` is `false`. `navigate()` on 
a config and `CodeGenConfig.navigate` answer the same way, so the lookup paths 
no longer disagree.
   
   ```groovy
   // server.url: http://localhost
   config.getProperty('server.url.port')                  // was 
'http://localhost', now null
   config.getProperty('server.url.port', String, 'none')  // 'none'
   config.containsProperty('server.url.port')             // false
   config.navigate('server', 'url', 'port')               // null
   ```
   
   **The flat key is read before the nested maps are walked.** `merge` and 
dot-notation assignment write every nested value twice, as a flat dotted key on 
the root and as an entry of the nested map, so the flat key answers nearly 
every lookup with one map read and the walk only runs for a key that has no 
flat form. Where the two representations have been made to disagree, the flat 
key wins:
   
   ```groovy
   config.merge([a: [b: 'merged']])
   config.put('a.b', 'flat')          // a raw root put: now read by 
getProperty, as it always was by config['a.b']
   config.getProperty('a.b')          // 'flat' (was 'merged')
   
   config.a.put('b', 'nested')        // a raw sub-map put: no longer read by 
getProperty
   config.getProperty('a.b')          // 'flat' (was 'nested'); config.a.b is 
'nested'
   
   config.a.b = 'assigned'            // dot-notation assignment writes both, 
so every read agrees
   config.getProperty('a.b')          // 'assigned'
   ```
   
   Reading the flat key first also exposed that the flat key never went through 
the placeholder hook: a YAML value `evaluatedName: ${name}` was resolved in the 
nested entry but kept the raw `${name}` in the flat key, which is what 
`config['hello.evaluatedName']` returned on every released version. The flat 
write now goes through the same `mergeMapEntry` hook as the nested one, so both 
representations hold the resolved value and every read agrees.
   
   The stale flat key that `merge` leaves when a higher-precedence scalar 
replaces a lower-precedence block, which is what property-source layering 
produces at startup, is read too: `merge(['server.url.port': 'y', 'server.url': 
'x'])` answers `server.url.port` with `y` (was `x`), and `put('a', 'scalar'); 
put('a.b', 1)` answers `a.b` with `1` (was `scalar`). The absent-past-non-map 
change alone already gives both of those the flat answer, so flat-first adds 
only which raw single-representation write wins.
   
   ## Measurements
   
   The harness from #16130 (two million `getProperty(key, String)` calls over 
four keys, three configured and one absent, best of five, 60 environment 
variables), run on this branch before and after the flat-first commit:
   
   | version | ns per call |
   |---|---|
   | this branch with #16130 and the absent-past-non-map walk | 129 |
   | plus reading the flat key first | 77 |
   
   ## Behaviour changes
   
   Both are described in section 17 of the 9.0 upgrade guide, with the required 
actions: read the parent instead of a child of a scalar, list or closure, and 
assign through dot notation instead of writing directly into a nested map when 
the value is read back through `getProperty`. The what's new entry for the 
faster lookups gained a sentence pointing there.
   
   ## Commits
   
   - The cherry-pick of the absent-past-scalar commit that #16130 reverted, 
extended to List and closure values and `containsProperty`.
   - `navigate` stops at a value that is not a map (`NavigableMap.navigateMap`, 
which `CodeGenConfig.navigate` uses).
   - The flat key read before the walk, with the four disagreement cases pinned.
   - Docs.
   - Placeholders resolved in the flat key as well, which the full grails-core 
suite asked for through `NavigableMapSpringProfilesSpec`.
   


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