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

Remove N+1 in sync #37439

Merged
merged 25 commits into from
Jan 16, 2024
Merged

Remove N+1 in sync #37439

merged 25 commits into from
Jan 16, 2024

Conversation

qnkhuat
Copy link
Contributor

@qnkhuat qnkhuat commented Jan 9, 2024

Closes #37250
During sync metadata process, we need to check whether or not we have select permissions against a table.
Currently we do that by making a select true from table limit 1 on the table. This is inefficient if we have a lot of tables.

In this PR, I've changed so that we rely on the information from driver/current-user-table-privileges to do the check.
This method was added recently in #32978.

Currently, it only supports Postgres,redshift, and MySQL, so only those drivers will benefit from this. We can later add support for more drivers by implementing that method.

Technical note: I've looked into using the getTablePrivileges from JDBC, but I decided to not using them because it does not return usage grant. So if someone has select privilege but not usage privilege, we still think they can select, but in reality, they can't.

@qnkhuat qnkhuat requested a review from camsaul as a code owner January 9, 2024 12:10
@metabase-bot metabase-bot bot added the .Team/BackendComponents also known as BEC label Jan 9, 2024
@qnkhuat qnkhuat changed the title Fix N+1 in sync Remove N+1 in sync Jan 9, 2024
@qnkhuat qnkhuat added the backport Automatically create PR on current release branch on merge label Jan 12, 2024
Copy link

replay-io bot commented Jan 12, 2024

StatusComplete ↗︎
Commitf27f17c
Results
⚠️ 2 Flaky
  • should allow non-user to unsubscribe from subscription
      AssertionError: Timed out retrying after 4000ms: Expected to find content: 'You've unsubscribed non-user@example.com from the "Orders in a dashboard" alert.' but never did.
          at Context.eval (http://localhost:4000/__cypress/tests?p=e2e/test/scenarios/sharing/subscriptions.cy.spec.js:45369:17)
      AssertionError: Timed out retrying after 4000ms: Expected to find content: 'You've unsubscribed non-user@example.com from the "Orders in a dashboard" alert.' but never did.
          at Context.eval (http://localhost:4000/__cypress/tests?p=e2e/test/scenarios/sharing/subscriptions.cy.spec.js:45369:17)
  • should order columns correctly in saved native query exports
      Timed out retrying after 4000ms: Unable to find an element with the text: Started from. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
      Ignored nodes: comments, <script />, <style />
      <html
        lang="en"
        translate="no"
      >
        <head>
          <meta
            charset="utf-8"
          />
          <meta
            content="IE=edge"
            http-equiv="X-UA-Compatible"
          />
          <meta
            content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"
            name="viewport"
          />
          <meta
            content="noindex"
            name="robots"
          />
          <meta
            content="yes"
            name="apple-mobile-web-app-capable"
          />
          <meta
            content="black-translucent"
            name="apple-mobile-web-app-status-bar-style"
          />
          <link
            href="app/assets/img/apple-touch-icon.png"
            rel="apple-touch-icon"
            sizes="180x180"
          />
          <link
            href="app/assets/img/favicon.ico"
            rel="icon"
          />
          <link
            crossorigin="use-credentials"
            href="app/assets/img/site.webmanifest"
            rel="manifest"
          />
          <meta
            content="#2d89ef"
            name="msapplication-TileColor"
          />
          <meta
            content="app/assets/img/browserconfig.xml"
            name="msapplication-config"
          />
          <meta
            content="#ffffff"
            name="theme-color"
          />
          <meta
            content="default"
            name="apple-mobile-web-app-status-bar-style"
          />
          <meta
            content="/"
            name="base-href"
          />
          <meta
            content="/question/80"
            name="uri"
          />
          <title>
            19889 · Metabase
          </title>
          <base
            href="/"
          />
          <link
            href="app/dist/styles.3ddf973fc967572ec34c.css"
            rel="stylesheet"
          />
          <link
            href="app/dist/app-main.8c3db53cb80ee9443da7.css"
            rel="stylesheet"
          />
        </head>
        <body>
          <div
            id="root"
          >
            <div
              class="spread emotion-1fttcpj enx0u2k2"
            >
              <header
                aria-label="Navigation bar"
                class="emotion-15xkiw5 es0756t0"
                data-testid="app-bar"
              >
                <div
                  class="emotion-1pt930v e4w71dr4"
                >
                  <div
                    class="emotion-cxb03 e4w71dr3"
                  >
                    <div
                      class="emotion-bjn8wh ehgm33i2"
                    >
                      <a
                        class="ehgm33i1 emotion-m53806 emeibx70"
                        data-metabase-event="Navbar;Logo"
                        href="/"
                      >
                        <svg
                          class="Icon text-brand"
                          data-testid="main-logo"
                          fill="currentcolor"
                          height="32"
                          viewBox="0 0 66 85"
                        >
                          <path
                            d="M46.8253288,70.4935014 C49.5764899,70.4935014 51.8067467,68.1774705 51.8067467,65.3205017 C51.8067467,62.4635329 49.5764899,60.147502 46.8253288,60.147502 C44.0741676,60.147502 41.8439108,62.4635329 41.8439108,65.3205017 C41.8439108,68.1774705 44.0741676,70.4935014 46.8253288,70.4935014 Z M32.8773585,84.9779005 C35.6285197,84.9779005 37.8587764,82.6618697 37.8587764,79.8049008 C37.8587764,76.947932 35.6285197,74.6319011 32.8773585,74.6319011 C30.1261973,74.6319011 27.8959405,76.947932 27.8959405,79.8049008 C27.8959405,82.6618697 30.1261973,84.9779005 32.8773585,84.9779005 Z M32.8773585,70.4935014 C35.6285197,70.4935014 37.8587764,68.1774705 37.8587764,65.3205017 C37.8587764,62.4635329 35.6285197,60.147502 32.8773585,60.147502 C30.1261973,60.147502 27.8959405,62.4635329 27.8959405,65.3205017 C27.8959405,68.1774705 30.1261973,70.4935014 32.8773585,70.4935014 Z M18.9293882,70.4935014 C21.6805494,70.4935014 23.9108062,68.1774705 23.9108062,65.3205017 C23.9108062,62.4635329 21.6805494,60.147502 18.9293882,60.147502 C16.1782271,60.147502 13.9479703,62.4635329 13.9479703,65.3205017 C13.9479703,68.1774705 16.1782271,70.4935014 18.9293882,70.4935014 Z M46.8253288,56.0091023 C49.5764899,56.0091023 51.8067467,53.6930714 51.8067467,50.8361026 C51.8067467,47.9791337 49.5764899,45.6631029 46.8253288,45.6631029 C44.0741676,45.6631029 41.8439108,47.9791337 41.8439108,50.8361026 C41.8439108,53.6930714 44.0741676,56.0091023 46.8253288,56.0091023 Z M18.9293882,56.0091023 C21.6805494,56.0091023 23.9108062,53.6930714 23.9108062,50.8361026 C23.9108062,47.9791337 21.6805494,45.6631029 18.9293882,45.6631029 C16.1782271,45.6631029 13.9479703,47.9791337 13.9479703,50.8361026 C13.9479703,53.6930714 16.1782271,56.0091023 18.9293882,56.0091023 Z M46.8253288,26.8995984 C49.5764899,26.8995984 51.8067467,24.5835675 51.8067467,21.7265987 C51.8067467,18.8696299 49.5764899,16.553599 46.8253288,16.553599 C44.0741676,16.553599 41.8439108,18.8696299 41.8439108,21.7265987 C41.8439108,24.5835675 44.0741676,26.8995984 46.8253288,26.8995984 Z M32.8773585,41.5247031 C35.6285197,41.5247031 37.8587764,39.2086723 37.8587764,36.3517034 C37.8587764,33.4947346 35.6285197,31.1787037 32.8773585,31.1787037 C30.1261973,31.1787037 27.8959405,33.4947346 27.8959405,36.3517034 C27.8959405,39.2086723 30.1261973,41.5247031 32.8773585,41.5247031 Z M32.8773585,10.3459994 C35.6285197,10.3459994 37.8587764,8.02996853 37.8587764,5.17299969 C37.8587764,2.31603085 35.6285197,0 32.8773585,0 C30.1261973,0 27.8959405,2.31603085 27.8959405,5.17299969 C27.8959405,8.02996853 30.1261973,10.3459994 32.8773585,10.3459994 Z M32.8773585,26.8995984 C35.6285197,26.8995984 37.8587764,24.5835675 37.8587764,21.7265987 C37.8587764,18.8696299 35.6285197,16.553599 32.8773585,16.553599 C30.1261973,16.553599 27.8959405,18.8696299 27.8959405,21.7265987 C27.8959405,24.5835675 30.1261973,26.8995984 32.8773585,26.8995984 Z M18.9293882,26.8995984 C21.6805494,26.8995984 23.9108062,24.5835675 23.9108062,21.7265987 C23.9108062,18.8696299 21.6805494,16.553599 18.9293882,16.553599 C16.1782271,16.553599 13.9479703,18.8696299 13.9479703,21.7265987 C13.9479703,24.5835675 16.1782271,26.8995984 18.9293882,26.8995984 Z"
                            opacity="0.2"
                          />
                          <path
                            d="M60.773299,70.4935014 C63.5244602,70.4935014 65.754717,68.1774705 65.754717,65.3205017 C65.754717,62.4635329 63.5244602,60.147502 60.773299,60.147502 C58.0221379,60.147502 55.7918811,62.4635329 55.7918811,65.3205017 C55.7918811,68.1774705 58.0221379,70.4935014 60.773299,70.4935014 Z M4.98141795,70.3527958 C7.73257912,70.3527958 9.96283591,68.0367649 9.96283591,65.1797961 C9.96283591,62.3228273 7.73257912,60.0067964 4.98141795,60.0067964 C2.23025679,60.0067964 0,62.3228273 0,65.1797961 C0,68.0367649 2.23025679,70.3527958 4.98141795,70.3527958 Z M60.773299,56.0091023 C63.5244602,56.0091023 65.754717,53.6930714 65.754717,50.8361026 C65.754717,47.9791337 63.5244602,45.6631029 60.773299,45.6631029 C58.0221379,45.6631029 55.7918811,47.9791337 55.7918811,50.8361026 C55.7918811,53.6930714 58.0221379,56.0091023 60.773299,56.0091023 Z M32.8773585,56.0091023 C35.6285197,56.0091023 37.8587764,53.6930714 37.8587764,50.8361026 C37.8587764,47.9791337 35.6285197,45.6631029 32.8773585,45.6631029 C30.1261973,45.6631029 27.8959405,47.9791337 27.8959405,50.8361026 C27.8959405,53.6930714 30.1261973,56.0091023 32.8773585,56.0091023 Z M4.98141795,55.8683967 C7.73257912,55.8683967 9.96283591,5...
2219 Passed

@Tony-metabase
Copy link
Contributor

This can be merged but me and @qnkhuat identified a new issue which might be the problem of the sync tables. It seems that the sync database schema now has 2 steps and if step 1 is still running the task skips to step 2, fyi:

image

I opened an issue for reference #37695

@@ -353,6 +353,27 @@
(testing "normally, ::fake-schema should be filtered out (because it does not exist)"
(is (not (contains? (schemas) fake-schema-name)))))))))))))

(deftest sync-materialized-views-test
Copy link
Contributor Author

Choose a reason for hiding this comment

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

added this as I noticed we didnt' have one

@@ -107,44 +106,77 @@
:schema (.getString rset "TABLE_SCHEM")
:description (when-let [remarks (.getString rset "REMARKS")]
(when-not (str/blank? remarks)
remarks))}))))))
remarks))
:type (.getString rset "TABLE_TYPE")}))))))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

current-user-table-privileges does not return privileges for external tables on redshift, and foreign table on postgres, so we need to use the select method on them, so to check select privilege for these tables we need to use the old method of trying a select query. In order to do that we need to return the type so we know what's the type of table we're dealing with.

see line 134.

(let [schema-filter-prop (driver.u/find-schema-filters-prop driver)
database (db-or-id-or-spec->database db-or-id-or-spec)]
(if (some? schema-filter-prop)
(let [prop-nm (:name schema-filter-prop)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed the branch were database is nil. doesn't make any sense to call describe-database on a database does not exist.

Copy link
Contributor

Choose a reason for hiding this comment

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

Did you check that all the callers of driver/describe-database don't break because of this change? I'm wondering why the code was written like this in the first place

Copy link
Contributor Author

@qnkhuat qnkhuat Jan 16, 2024

Choose a reason for hiding this comment

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

I think it was designed so that it can take a spec as argument. I don't see anywhere that's used in production, only in tests.

but to be safe I will revert this change and follow up on this later. don't want another PR to have unnesscary change then turns out it's nessceary like #37620 :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we already have tests for syncing view and foreign table in this namespace.

@qnkhuat qnkhuat requested a review from a team January 15, 2024 15:36
Copy link
Contributor

@calherries calherries left a comment

Choose a reason for hiding this comment

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

Looks good, all my comments are nits really. This is mergeable even if you want to dismiss some of them

src/metabase/driver/sql_jdbc/sync/describe_database.clj Outdated Show resolved Hide resolved
src/metabase/driver/postgres.clj Outdated Show resolved Hide resolved
src/metabase/driver/postgres.clj Outdated Show resolved Hide resolved
src/metabase/sync/sync_metadata/sync_table_privileges.clj Outdated Show resolved Hide resolved
src/metabase/driver/sql_jdbc/sync/describe_database.clj Outdated Show resolved Hide resolved
(let [schema-filter-prop (driver.u/find-schema-filters-prop driver)
database (db-or-id-or-spec->database db-or-id-or-spec)]
(if (some? schema-filter-prop)
(let [prop-nm (:name schema-filter-prop)
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you check that all the callers of driver/describe-database don't break because of this change? I'm wondering why the code was written like this in the first place

test/metabase/driver/postgres_test.clj Show resolved Hide resolved
(when-not (.getAutoCommit conn)
(.rollback conn))
false))))
(execute-select-probe-query driver conn sql-args)
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation was fine before

table-names (->> (jdbc/query conn-spec "SHOW TABLES" {:as-arrays? true})
(drop 1)
(map first))]
(for [[table-name privileges] (table-names->privileges (privilege-grants-for-user conn-spec "CURRENT_USER()")
(:name database)
db-name
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@calherries, This was a bug that we didn't catch in our BE tests.
we should use the db from details, not the display name. I only catch it because the e2e tests use a different display name on its test database.

see my change in test/metabase/driver/mysql_test.clj to make sure we covered this in tests.

@qnkhuat qnkhuat merged commit 0a54da9 into master Jan 16, 2024
105 checks passed
@qnkhuat qnkhuat deleted the ngoc-remove-n-plus-1-in-sync branch January 16, 2024 14:27
Copy link

@qnkhuat Did you forget to add a milestone to the issue for this PR? When and where should I add a milestone?

qnkhuat added a commit that referenced this pull request Jan 16, 2024
qnkhuat added a commit that referenced this pull request Feb 19, 2024
using the resutl from `driver/current-user-table-privileges` method.

But we see some users reported tables are disabled during sync, while I
haven't found out the root cause of that, I think we should added a
fallback to using the old privilege check if the new method return
`false`. This way we still get the performance boost but is safer when
introducing this new method.

The only downside is it's hard to know why the new method give false
positive results. With this we either rely on someone see our warn log
and log an issue, or maybe we'll collect some telemetry using snowplow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport Automatically create PR on current release branch on merge .Team/BackendComponents also known as BEC
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sync takes a massive amount of time to start
4 participants