Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add has_one scope support to virtual_delegate #17473

Merged

Conversation

NickLaMuro
Copy link
Member

@NickLaMuro NickLaMuro commented May 23, 2018

This enhances virtual_delegates so that scopes added as part of a has_one relation are respected.

Because of the changes, an implicit limit = 1 was necessary to support the has_one, and the easiest way to accomplish that while still keeping select_from_alias in a similar form was to allow passing a block so the arel (prior to being converted to a string) could be updated.

Also of note, select_from_alias also had a few changes to it beyond that:

  • The "query" is now built up from the scope on the model, not a raw arel_table from the to_ref model.
  • Instead of a project, we call a .select on the "query" (scope from the relation), which already has a similar arg structure, but allows us to remove the select_values properly before converting it to arel (happens right after the .select). We work with raw Arel after that.
  • The table_alias functionality is now done through the from clause, from(to_table) allows us to still keep the same alias functionality we had. Under the covers, this does the same thing, it is just previously the starting point was an Arel::Table, and now it is a Arel::SelectManager. The Arel::Table version ends up returning a SelectManager anyway, so what we are doing is functionally equivalent.

Links

TODO

Should probably add some tests around this. That said, this is a for a semi POC for an alternative to #17469, so the POC should probably be validated against before commiting to the changes here.

@NickLaMuro
Copy link
Member Author

@miq-bot assign @kbrock

I have two more followup PRs that will add some context to this one.

@NickLaMuro NickLaMuro changed the title Add has_one scope support to virtual_delegate [WIP] Add has_one scope support to virtual_delegate May 23, 2018
@miq-bot miq-bot added the wip label May 23, 2018
Copy link
Member

@kbrock kbrock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sweet.
Can't wait to see a test and see the WIP removed

@@ -287,10 +289,22 @@ def virtual_delegate_arel(col, to_ref)
#

def self.select_from_alias(to_ref, col, to_model_col_name, src_model_id)
to_table = select_from_alias_table(to_ref.klass, src_model_id.relation)
query = if to_ref.scope
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice to base upon the to_ref association.

This enhances virtual_delegates so that scopes added as part of a
`has_one` relation are respected.

Because of the changes, an implicit `limit = 1` was necessary to support
the `has_one`, and the easiest way to accomplish that while still
keeping `select_from_alias` in a similar form was to allow passing a
block so the arel (prior to being converted to a string) could be
updated.

Also of note, `select_from_alias` also had a few changes to it beyond
that:

  - The "query" is now built up from the scope on the model, not a raw
    arel_table from the to_ref model.
  - Instead of a `project`, we call a `.select` on the "query" (scope
    from the relation), which already has a similar arg structure, but
    allows us to remove the select_values properly before converting it
    to arel (happens right after the `.select`).  We work with raw
    `Arel` after that.
  - The `table_alias` functionality is now done through the `from`
    clause, `from(to_table)` allows us to still keep the same alias
    functionality we had.  Under the covers, this does the same thing,
    it is just previously the starting point was an `Arel::Table`, and
    now it is a `Arel::SelectManager`.  The `Arel::Table` version ends
    up returning a `SelectManager` anyway, so what we are doing is
    functionally equivalent.
@NickLaMuro NickLaMuro force-pushed the virtual_delegate_has_one_scope_support branch from eb62cba to 836ea6a Compare June 8, 2018 16:31
@NickLaMuro NickLaMuro changed the title [WIP] Add has_one scope support to virtual_delegate Add has_one scope support to virtual_delegate Jun 8, 2018
@miq-bot miq-bot removed the wip label Jun 8, 2018
@NickLaMuro NickLaMuro force-pushed the virtual_delegate_has_one_scope_support branch from 836ea6a to fec164a Compare June 8, 2018 18:14
@miq-bot
Copy link
Member

miq-bot commented Jun 8, 2018

Checked commits NickLaMuro/manageiq@f33a768~...fec164a with ruby 2.3.3, rubocop 0.52.1, haml-lint 0.20.0, and yamllint 1.10.0
2 files checked, 0 offenses detected
Everything looks fine. 🍪

@kbrock kbrock merged commit 0ce8fb4 into ManageIQ:master Jun 11, 2018
@kbrock kbrock added this to the Sprint 88 Ending Jun 18, 2018 milestone Jun 11, 2018
NickLaMuro added a commit to NickLaMuro/manageiq-ui-classic that referenced this pull request Feb 26, 2019
Over time, the `compliances` table will get quiet large, and when paired
with the rest of the tables in this report, you end up getting a query
executed that looks like this:

    SELECT "vms"."id" AS t0_r0, ..., "vms"."memory_hot_add_increment" AS t0_r75,
           "hosts"."id" AS t1_r0, ..., "hosts"."physical_server_id" AS t1_r36,
           "storages"."id" AS t2_r0, ..., "storages"."storage_domain_type" AS t2_r18,
           "ext_management_systems"."id" AS t3_r0, ..., "ext_management_systems"."tenant_mapping_enabled" AS t3_r23,
           "snapshots"."id" AS t4_r0, ..., "snapshots"."ems_ref" AS t4_r16,
           "compliances"."id" AS t5_r0, ..., "compliances"."event_type" AS t5_r6,
           "operating_systems"."id" AS t6_r0, ..., "operating_systems"."kernel_version" AS t6_r25,
           "hardwares"."id" AS t7_r0, ..., "hardwares"."provision_state" AS t7_r34,
           "tags"."id" AS t8_r0, "tags"."name" AS t8_r1
    FROM "vms"
    LEFT OUTER JOIN "hosts" ON "hosts"."id" = "vms"."host_id"
    LEFT OUTER JOIN "storages" ON "storages"."id" = "vms"."storage_id"
    LEFT OUTER JOIN "ext_management_systems" ON "ext_management_systems"."id" = "vms"."ems_id"
    LEFT OUTER JOIN "snapshots" ON "snapshots"."vm_or_template_id" = "vms"."id"
    LEFT OUTER JOIN "compliances" ON "compliances"."resource_id" = "vms"."id" AND "compliances"."resource_type" = $1
    LEFT OUTER JOIN "operating_systems" ON "operating_systems"."vm_or_template_id" = "vms"."id"
    LEFT OUTER JOIN "hardwares" ON "hardwares"."vm_or_template_id" = "vms"."id"
    LEFT OUTER JOIN "taggings" ON "taggings"."taggable_id" = "vms"."id" AND "taggings"."taggable_type" = $2
    LEFT OUTER JOIN "tags" ON "tags"."id" = "taggings"."tag_id"
    WHERE "vms"."type" IN (...)
      AND "vms"."template" = $3
      AND (lower(vms.name) like '%win2k%' escape '`')
      AND "vms"."id" IN (...)
    ORDER BY LOWER("vms"."name") ASC

This ends up creating 243 columns per record, and the number of rows
returned has been observed to be over 80k with just 20 VMs being
targeted in the `"vms"."id" IN (...)` portion of the query.

Since some fixes have been made to avoid the N+1 that results from doing
this:

- ManageIQ/manageiq#17473
- ManageIQ/manageiq#17474
- ManageIQ/manageiq#17475

It is much better to do use those facilities.  Even with the N+1, it is
much better making extra round trips than the gigs of data that would be
returned as a result.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants