From 06531e01c5d195fdf5a0865fdcd423d03be06fd5 Mon Sep 17 00:00:00 2001 From: Quentin Machu Date: Fri, 19 Feb 2016 20:55:54 -0500 Subject: [PATCH] database: disable hash/merge joins in FindLayer Our experiments have shown that PostgreSQL 9.4 makes bad planning decisions about: - joining the layer tree to feature versions and feature - joining the feature versions to affected/fixed feature version and vulnerabilities It would for instance do a merge join between affected feature versions (300 rows, estimated 3000 rows) and fixed in feature version (100k rows). In this case, it is much more preferred to use a nested loop. --- database/pgsql/layer.go | 34 ++++++++++++++++++++++++++++------ database/pgsql/queries.go | 2 ++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/database/pgsql/layer.go b/database/pgsql/layer.go index f8c9cac8..2581d14d 100644 --- a/database/pgsql/layer.go +++ b/database/pgsql/layer.go @@ -65,8 +65,30 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo // Find its features if withFeatures || withVulnerabilities { + // Create a transaction to disable hash/merge joins as our experiments have shown that + // PostgreSQL 9.4 makes bad planning decisions about: + // - joining the layer tree to feature versions and feature + // - joining the feature versions to affected/fixed feature version and vulnerabilities + // It would for instance do a merge join between affected feature versions (300 rows, estimated + // 3000 rows) and fixed in feature version (100k rows). In this case, it is much more + // preferred to use a nested loop. + tx, err := pgSQL.Begin() + if err != nil { + return layer, handleError("FindLayer.Begin()", err) + } + defer tx.Commit() + + _, err = tx.Exec(getQuery("disable_hashjoin")) + if err != nil { + log.Warningf("FindLayer: could not disable hash join: %s", err) + } + _, err = tx.Exec(getQuery("disable_mergejoin")) + if err != nil { + log.Warningf("FindLayer: could not disable merge join: %s", err) + } + t = time.Now() - featureVersions, err := pgSQL.getLayerFeatureVersions(layer.ID) + featureVersions, err := getLayerFeatureVersions(tx, layer.ID) observeQueryTime("FindLayer", "getLayerFeatureVersions", t) if err != nil { @@ -78,7 +100,7 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo if withVulnerabilities { // Load the vulnerabilities that affect the FeatureVersions. t = time.Now() - err := pgSQL.loadAffectedBy(layer.Features) + err := loadAffectedBy(tx, layer.Features) observeQueryTime("FindLayer", "loadAffectedBy", t) if err != nil { @@ -91,11 +113,11 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo } // getLayerFeatureVersions returns list of database.FeatureVersion that a database.Layer has. -func (pgSQL *pgSQL) getLayerFeatureVersions(layerID int) ([]database.FeatureVersion, error) { +func getLayerFeatureVersions(tx *sql.Tx, layerID int) ([]database.FeatureVersion, error) { var featureVersions []database.FeatureVersion // Query. - rows, err := pgSQL.Query(getQuery("s_layer_featureversion"), layerID) + rows, err := tx.Query(getQuery("s_layer_featureversion"), layerID) if err != nil { return featureVersions, handleError("s_layer_featureversion", err) } @@ -140,7 +162,7 @@ func (pgSQL *pgSQL) getLayerFeatureVersions(layerID int) ([]database.FeatureVers // loadAffectedBy returns the list of database.Vulnerability that affect the given // FeatureVersion. -func (pgSQL *pgSQL) loadAffectedBy(featureVersions []database.FeatureVersion) error { +func loadAffectedBy(tx *sql.Tx, featureVersions []database.FeatureVersion) error { if len(featureVersions) == 0 { return nil } @@ -151,7 +173,7 @@ func (pgSQL *pgSQL) loadAffectedBy(featureVersions []database.FeatureVersion) er featureVersionIDs = append(featureVersionIDs, featureVersions[i].ID) } - rows, err := pgSQL.Query(getQuery("s_featureversions_vulnerabilities"), + rows, err := tx.Query(getQuery("s_featureversions_vulnerabilities"), buildInputArray(featureVersionIDs)) if err != nil && err != sql.ErrNoRows { return handleError("s_featureversions_vulnerabilities", err) diff --git a/database/pgsql/queries.go b/database/pgsql/queries.go index 6d090a33..ae84bb1d 100644 --- a/database/pgsql/queries.go +++ b/database/pgsql/queries.go @@ -25,6 +25,8 @@ func init() { queries = make(map[string]string) queries["l_vulnerability_affects_featureversion"] = `LOCK Vulnerability_Affects_FeatureVersion IN SHARE ROW EXCLUSIVE MODE` + queries["disable_hashjoin"] = `SET LOCAL enable_hashjoin = off` + queries["disable_mergejoin"] = `SET LOCAL enable_mergejoin = off` // keyvalue.go queries["u_keyvalue"] = `UPDATE KeyValue SET value = $1 WHERE key = $2`