Remove NodeHooks1

Created on 6 March 2025, about 1 month ago

Problem/Motivation

For some reason we have a duplicate file in core.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component

node system

Created by

πŸ‡¬πŸ‡§United Kingdom catch

Live updates comments and jobs are added and updated live.
  • Novice

    It would make a good project for someone who is new to the Drupal contribution process. It's preferred over Newbie.

Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @catch
  • πŸ‡¬πŸ‡§United Kingdom catch

    It's not a 100% duplicate, e.g. query_node_access_alter only appears to be in this file. Might be necessary to merge it with NodeHooks.

  • πŸ‡§πŸ‡·Brazil charlliequadros

    Hi @catch
    Is the file name "nodehooks1"?
    I couldn't find it. Could you tell me where it is located?

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    I don't think it's a duplicate at all? The original NodeHooks.php was added in πŸ“Œ OOP hooks using event dispatcher Needs review , I believe as a demonstration of implementing the same hook twice (hook_user_cancel). The newer NodeHooks1.php was added in πŸ“Œ [ignore] Convert everything everywhere all at once Active when all the remaining hooks were converted. I think we should move the 2 hooks in NodeHooks to NodeHooks1, delete NodeHooks and then rename NodeHooks1 to NodeHooks. Any further refactoring should be done later.

  • Merge request !11406Combine NodeHooks and NodeHooks1 β†’ (Open) created by mstrelan
  • Pipeline finished with Failed
    about 1 month ago
    Total: 135s
    #442419
  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    Sorry, didn't see the Novice tag. Should have left it.

  • Pipeline finished with Failed
    about 1 month ago
    Total: 1256s
    #442421
  • πŸ‡¬πŸ‡§United Kingdom catch

    Given it's not a duplicate it's also not a novice issue.

  • First commit to issue fork.
  • Pipeline finished with Failed
    about 1 month ago
    Total: 951s
    #444778
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    So re-ran a few times but some performance tests are consistently failing

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    Drupal\Tests\navigation\FunctionalJavascript\PerformanceTest::testLogin

    I put a breakpoint in \Drupal\Tests\PerformanceTestTrait::collectPerformanceData and compared $performance_test_data['cache_operations'] between HEAD and this issue. These are the 2 additional cache gets:

    {
      "operation": "get",
      "cids": "entity_type_definitions.installed",
      "bin": "discovery",
      "start": 1741650201.981434,
      "stop": 1741650201.984118
    },
    {
      "operation": "get",
      "cids": "node.field_storage_definitions.installed",
      "bin": "discovery",
      "start": 1741650201.984128,
      "stop": 1741650201.985614
    }
    

    Drupal\Tests\standard\FunctionalJavascript\StandardPerformanceTest::testStandardPerformance

    The diff in the array comparison is:

    [
        2 => 'SELECT "cid", "data", "created", "expire", "serialized", "tags", "checksum" FROM "cache_discovery" WHERE "cid" IN ( "entity_type_definitions.installed" ) ORDER BY "cid"',
        3 => 'SELECT "cid", "data", "created", "expire", "serialized", "tags", "checksum" FROM "cache_discovery" WHERE "cid" IN ( "node.field_storage_definitions.installed" ) ORDER BY "cid"',
    ]
    

    So it looks like both tests are failing for the same reasons, i.e. entity_type_definitions.installed and node.field_storage_definitions.installed.

    I would imagine this is because of dependency injection. Previously we were injecting the entity type manager and loading node storage in NodeHooks.php, but this was only ever invoked for hook_user_cancel. Now that this is combined with NodeHooks1 we're injecting these for far more hooks. I think in this case we can simply update the performance test expectations, but this also highlights why it might be worth splitting hooks classes based on what dependencies are required. Alternatively, we could revert to using Drupal::service here to reduce the number of cache gets here.

    Also was running in to some other issues but they seem to be fixed in πŸ“Œ Remove cache tag checksum assertions from performance tests Active , so I merged in 11.x again.

  • Pipeline finished with Failed
    about 1 month ago
    Total: 889s
    #445199
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Update to performance tests appear to fix everything.

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    acbramley β†’ changed the visibility of the branch 11.x to hidden.

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    Based on the description in #10 I don't know if I agree with combining these all into 1 class.

    We now have 21 hooks in a single class, 19 of which don't need the injected services (all from NodeHooks1).

    IMO we could just rename NodeHooks1 to something a bit more descriptive? We don't have to shove every single hook into a single class.

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    Need an IS update too as it's not a duplicate file.

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    One idea I had was whether we could make all hooks that don't use services into static functions, and then we could group those hooks into NodeStaticHooks. I've tested this and it does seem to work, however I don't know if there's any performance benefit from making them static, but it seems like it might?

  • πŸ‡³πŸ‡ΏNew Zealand quietone

    Closed πŸ“Œ Rename NodeHooks1 to something meaningful Active as a duplicate.

  • πŸ‡¬πŸ‡§United Kingdom catch

    Yes #10 makes this tricky, we should at least make sure the extra service instantiations aren't in the critical path.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Just a quick note, this only exists because of a file exists when running rector the script creates a new file and increments. When I ran core we had only created the one core conversion in NodeHooks so NodeHooks1 was created.

    There is no duplication it is just manual vs automated conversions.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    Thanks @nicxvan, that's the same conclusion I came to in #4. I've updated the IS to clarify.

    I've pushed another branch to see how the tests look if we remove dependency injection in this class. There are already plenty of other services in the class accessed by \Drupal::*.

  • Pipeline finished with Success
    about 1 month ago
    Total: 2247s
    #450707
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    No need to do this in two steps, I think we're leaning towards naming the file based on the api where the hook is defined.

    Then we can add DI for each.

    If there is a problematic one we can split further. I've added the meta.

  • Merge request !11511#3511357 NodeHooks with DI for all the things β†’ (Open) created by mstrelan
  • Pipeline finished with Failed
    about 1 month ago
    Total: 190s
    #450782
  • Pipeline finished with Failed
    about 1 month ago
    #450784
  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    OK that's good to know. I had another idea that we could use service closures and the #[AutowireServiceClosure] attribute in hook classes. I replaced every \Drupal:: call in NodeHooks with a service closure and re-ran the performance tests. This resulted in the same numbers as when using the static everywhere. Then I switched to directly injecting all the services and ran the tests again, expecting a worse score, but again it was the same as using static everywhere. Not sure what that tells us, injecting some services results in a performance regression but injecting all services does not. I couldn't get all the tests passing though, so maybe I've broken something.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Sorry, we do need to split the file though, we don't want to be injecting everything on every hook.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    Totally agree. The point of that MR was to compare performance. For some reason MR !11406 requires adjustments to existing performance tests when injecting two services, but that same test passes in MR !11511 without adjustments when injecting all services. So I'm trying to figure out why there is a discrepancy.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    mstrelan β†’ changed the visibility of the branch 3511357-nodehooks-di to hidden.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    I figured out why there was a performance regression in some branches but not others. In the first MR I had kept the node storage load in the constructor, even after I commented that it was an anti-pattern. But in the additional branches I had abstracted that out and only loaded when needed.

    I've updated the original MR to not load the node storage in the constructor and reverted the expected performance test metrics to the original values.

    Going to leave it there to decide if we want to proceed with this or split things further as per #21.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Glad you sorted that out!

    I think my general recommendation is going to be a 3 part solution for all hooks.

    1. Rough conversion, i.e. convert as close to exact as possible, (lift and shift) This is done for almost all hooks in core.
    2. Rough organization, DI, t(), Split out by *.api.php, add DI, convert to $this->t()
    3. Fine tuning - individual refactors and split out critical path performance hooks

    I am going to catalog the breakdown for the other issue and move these over. Since this is step 2 I'd advocate we split the files out by api.php

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    If we split by api.php it will look like this:

    NodeConfigTranslationHooks
    Hooks: configTranslationInfoAlter
    Services: n/a

    NodeCoreHooks
    Hooks: cron
    Services: module_handler, entity_type_manager, state

    NodeDatabaseHooks
    Hooks: queryNodeAccessAlter
    Services: current_user, module_handler, entity_type_manager, node.grant_storage, renderer

    NodeEntityHooks
    Hooks: userPredelete, configurableLanguageDelete, commentInsert, commentUpdate, commentDelete, entityViewDisplayAlter, entityExtraFieldInfo, nodeAccess
    Services: entity_type_manager
    Note: Only the first two hooks need the service

    NodeHelpHooks
    Hooks: hook_help
    Services: current_user, messenger

    NodeMenuHooks
    Hooks: localTasksAlter
    Services: n/a

    NodeModuleHooks
    Hooks: modulesInstalled, modulesUninstalled
    Services: moduleHandler

    NodeNodeHooks
    Hooks: ranking
    Services: state

    NodeThemeHooks
    Hooks: pageTop, formSystemThemesAdminFormAlter, theme
    Services: current_route_match, form_builder, config.factory
    Note: pageTop doesn't require config.factory but it would be injected pretty much everywhere. Also theme has no requirements.

    NodeUserHooks
    Hooks: userCancelBlockUnpublish, userCancelReassign
    Services: entity_type_manager, module_handler

    So we will be splitting to 10 new classes. Some of these make a lot of sense, like NodeModuleHooks. But NodeEntityHooks looks like it could benefit from splitting further, e.g. userPredelete could go to NodeUserHooks so we dont need entity_type_manager there.

    I don't really have an opinion on the matter, but if it's agreed this is what we should do then I can work on it.

  • Pipeline finished with Success
    about 1 month ago
    Total: 3373s
    #450816
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Wow that was quick!
    I think that looks great!

    But NodeEntityHooks looks like it could benefit from splitting further, e.g. userPredelete could go to NodeUserHooks so we dont need entity_type_manager there.

    That would be part of step 3.
    I don't have a super strong opinion, but I think keeping it to three steps will reduce bike shedding while getting the improvements we want in a reasonable time.

    For example, I wouldn't put those with user hooks since they are not part of that, I'd make it part of another class like: NodeEntityHooks and NodeEntityDeleteHooks, then I would add the three delete hooks even if only two need the injection.

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Just throwing my 2cents in but I really like how πŸ“Œ Clean up hook implementations in the Taxonomy module Active got organized. Maybe can do something similar.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    29 is following the same logic as πŸ“Œ Clean up hook implementations in the Taxonomy module Active

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Let’s do that then :)

Production build 0.71.5 2024