[lxc-devel] [lxd/master] Improve performance of setting volatile keys

stgraber on Github lxc-bot at linuxcontainers.org
Thu May 9 19:15:30 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190509/b21318e0/attachment.bin>
-------------- next part --------------
From 3f024abeae722ffc2fe1f62fed15a4ef1d45e526 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 9 May 2019 15:13:56 -0400
Subject: [PATCH 1/2] lxd/db: Introduce ContainerConfigUpdate
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/db/containers.go | 48 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index 152df5d2df..8b453e7225 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -524,6 +524,54 @@ func (c *ClusterTx) ContainerConfigInsert(id int, config map[string]string) erro
 	return ContainerConfigInsert(c.tx, id, config)
 }
 
+// ContainerConfigUpdate inserts/updates/deletes the provided keys
+func (c *ClusterTx) ContainerConfigUpdate(id int, values map[string]string) error {
+	changes := map[string]string{}
+	deletes := []string{}
+
+	// Figure out which key to set/unset
+	for key, value := range values {
+		if value == "" {
+			deletes = append(deletes, key)
+			continue
+		}
+		changes[key] = value
+	}
+
+	// Insert/update keys
+	if len(changes) > 0 {
+		query := fmt.Sprintf("INSERT OR REPLACE INTO containers_config (container_id, key, value) VALUES")
+		exprs := []string{}
+		params := []interface{}{}
+		for key, value := range changes {
+			exprs = append(exprs, "(?, ?, ?)")
+			params = append(params, []interface{}{id, key, value}...)
+		}
+
+		query += strings.Join(exprs, ",")
+		_, err := c.tx.Exec(query, params...)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Delete keys
+	if len(deletes) > 0 {
+		query := fmt.Sprintf("DELETE FROM containers_config WHERE key IN %s AND container_id=?", query.Params(len(deletes)))
+		params := []interface{}{}
+		for _, key := range deletes {
+			params = append(params, key)
+		}
+
+		_, err := c.tx.Exec(query, params...)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 // ContainerRemove removes the container with the given name from the database.
 func (c *Cluster) ContainerRemove(project, name string) error {
 	return c.Transaction(func(tx *ClusterTx) error {

From d89d1f20bc4ef969fffd096d0f86afe89511678e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 9 May 2019 15:14:23 -0400
Subject: [PATCH 2/2] lxd/containers: Replace ConfigKeySet with VolatileSet
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container.go     |  4 ++--
 lxd/container_lxc.go | 49 +++++++++++++++++++++++++-------------------
 lxd/containers.go    |  4 ++--
 lxd/storage.go       |  2 +-
 4 files changed, 33 insertions(+), 26 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 1a6a1b4532..7295d06917 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -637,7 +637,7 @@ type container interface {
 	// Live configuration
 	CGroupGet(key string) (string, error)
 	CGroupSet(key string, value string) error
-	ConfigKeySet(key string, value string) error
+	VolatileSet(changes map[string]string) error
 
 	// File handling
 	FileExists(path string) error
@@ -1349,7 +1349,7 @@ func containerConfigureInternal(c container) error {
 	if rootDiskDevice["size"] != "" {
 		storageTypeName := storage.GetStorageTypeName()
 		if (storageTypeName == "lvm" || storageTypeName == "ceph") && c.IsRunning() {
-			err = c.ConfigKeySet("volatile.apply_quota", rootDiskDevice["size"])
+			err = c.VolatileSet(map[string]string{"volatile.apply_quota": rootDiskDevice["size"]})
 			if err != nil {
 				return err
 			}
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 1a65e9c47a..481cec94d7 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -456,14 +456,14 @@ func containerLXCCreate(s *state.State, args db.ContainerArgs) (container, error
 		jsonIdmap = "[]"
 	}
 
-	err = c.ConfigKeySet("volatile.idmap.next", jsonIdmap)
+	err = c.VolatileSet(map[string]string{"volatile.idmap.next": jsonIdmap})
 	if err != nil {
 		c.Delete()
 		logger.Error("Failed creating container", ctxMap)
 		return nil, err
 	}
 
-	err = c.ConfigKeySet("volatile.idmap.base", fmt.Sprintf("%v", base))
+	err = c.VolatileSet(map[string]string{"volatile.idmap.base": fmt.Sprintf("%v", base)})
 	if err != nil {
 		c.Delete()
 		logger.Error("Failed creating container", ctxMap)
@@ -475,7 +475,7 @@ func containerLXCCreate(s *state.State, args db.ContainerArgs) (container, error
 
 	// Set last_state if not currently set
 	if c.localConfig["volatile.last_state.idmap"] == "" {
-		err = c.ConfigKeySet("volatile.last_state.idmap", "[]")
+		err = c.VolatileSet(map[string]string{"volatile.last_state.idmap": "[]"})
 		if err != nil {
 			c.Delete()
 			logger.Error("Failed creating container", ctxMap)
@@ -2158,7 +2158,7 @@ func (c *containerLXC) startCommon() (string, error) {
 			jsonDiskIdmap = string(idmapBytes)
 		}
 
-		err = c.ConfigKeySet("volatile.last_state.idmap", jsonDiskIdmap)
+		err = c.VolatileSet(map[string]string{"volatile.last_state.idmap": jsonDiskIdmap})
 		if err != nil {
 			return "", errors.Wrapf(err, "Set volatile.last_state.idmap config key on container %q (id %d)", c.name, c.id)
 		}
@@ -2177,7 +2177,7 @@ func (c *containerLXC) startCommon() (string, error) {
 	}
 
 	if c.localConfig["volatile.idmap.current"] != string(idmapBytes) {
-		err = c.ConfigKeySet("volatile.idmap.current", string(idmapBytes))
+		err = c.VolatileSet(map[string]string{"volatile.idmap.current": string(idmapBytes)})
 		if err != nil {
 			return "", errors.Wrapf(err, "Set volatile.idmap.current config key on container %q (id %d)", c.name, c.id)
 		}
@@ -3965,26 +3965,33 @@ func (c *containerLXC) CGroupSet(key string, value string) error {
 	return nil
 }
 
-func (c *containerLXC) ConfigKeySet(key string, value string) error {
-	c.localConfig[key] = value
-
-	args := db.ContainerArgs{
-		Architecture: c.architecture,
-		Config:       c.localConfig,
-		Description:  c.description,
-		Devices:      c.localDevices,
-		Ephemeral:    c.ephemeral,
-		Profiles:     c.profiles,
-		Project:      c.project,
-		ExpiryDate:   c.expiryDate,
+func (c *containerLXC) VolatileSet(changes map[string]string) error {
+	// Sanity check
+	for key := range changes {
+		if !strings.HasPrefix(key, "volatile.") {
+			return fmt.Errorf("Only volatile keys can be modified with VolatileSet")
+		}
 	}
 
-	err := c.Update(args, false)
+	// Update the database
+	err := c.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		return tx.ContainerConfigUpdate(c.id, changes)
+	})
 	if err != nil {
-		errors.Wrap(err, "Failed to update container")
+		return errors.Wrap(err, "Failed to update database")
 	}
 
-	return err
+	// Apply the change locally
+	for key, value := range changes {
+		if value == "" {
+			delete(c.localConfig, key)
+			continue
+		}
+
+		c.localConfig[key] = value
+	}
+
+	return nil
 }
 
 type backupFile struct {
@@ -5730,7 +5737,7 @@ func (c *containerLXC) TemplateApply(trigger string) error {
 	// "create" and "copy" are deferred until next start
 	if shared.StringInSlice(trigger, []string{"create", "copy"}) {
 		// The two events are mutually exclusive so only keep the last one
-		err := c.ConfigKeySet("volatile.apply_template", trigger)
+		err := c.VolatileSet(map[string]string{"volatile.apply_template": trigger})
 		if err != nil {
 			return errors.Wrap(err, "Failed to set apply_template volatile key")
 		}
diff --git a/lxd/containers.go b/lxd/containers.go
index 49625cfd0b..4f541ba0dd 100644
--- a/lxd/containers.go
+++ b/lxd/containers.go
@@ -290,12 +290,12 @@ func containersShutdown(s *state.State) error {
 			go func(c container, lastState string) {
 				c.Shutdown(time.Second * time.Duration(timeoutSeconds))
 				c.Stop(false)
-				c.ConfigKeySet("volatile.last_state.power", lastState)
+				c.VolatileSet(map[string]string{"volatile.last_state.power": lastState})
 
 				wg.Done()
 			}(c, lastState)
 		} else {
-			c.ConfigKeySet("volatile.last_state.power", lastState)
+			c.VolatileSet(map[string]string{"volatile.last_state.power": lastState})
 		}
 	}
 	wg.Wait()
diff --git a/lxd/storage.go b/lxd/storage.go
index 2e07d53039..f8c7e70c45 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -777,7 +777,7 @@ func resetContainerDiskIdmap(container container, srcIdmap *idmap.IdmapSet) erro
 			jsonIdmap = "[]"
 		}
 
-		err := container.ConfigKeySet("volatile.last_state.idmap", jsonIdmap)
+		err := container.VolatileSet(map[string]string{"volatile.last_state.idmap": jsonIdmap})
 		if err != nil {
 			return err
 		}


More information about the lxc-devel mailing list