openSUSE:WebYaST Caching

Jump to: navigation, search

WebYaST Caching

How it works

The last view releases have shown that the backend of WebYaST is too slow. Especially starting new modules takes very often too much time.

A system management tools is based on data which will not be changed very often ( e.g. user, printer, network settings) but consumes very much time for evaluating. So the idea is to preload this data while starting WebYaST in a cache which can be used at once.

If the cache is used by the UI a job will be started parallel to evaluate this data again from the system. If the new evaluated data differs from the cache the UI/user will be informed and the UI will be reloaded:


WebYaST http-communication HTTP browser
* starting with: rcwebyast start
* Go through all models, calls the find methods,
and insert the results into the cache:
User.find(:all)
Network.find(:all)
Ldap.find(:all)
...
..
.

* Showing e.g. the User' index page
<--- GET User/ --- * Getting all Users from WebYaST
* Reading Cache for User.find(:all) --- User.find(:all) ---> * Showing all Users in the UI
* Calling User.find(:all) in order to check and update cache.
<--- GET notifier/user --- * Polling if the cache has been changed meanwhile.
* Check if the User cache has been changed ? --- yes/no --> * Update UI if the date has been changed meanwhile
* Polling for changed cache periodically
...
..
.

The nice "side-effect" is that other UIs will also be informed if some data has been changed in the background meanwhile. E.g. two users edit the network configuration at the same time. After one user has saved his values the second user will be informed that the data has been changed and he will get an update.

How it is implemented

If you want to use this cache mechanism in your plugin you have to change following methods in your model class. This is an example for the Group model class (file: plugins/users/app/models/group.rb):

require 'yast_service'

Includes needed libs.

  def self.find (cn)
    return find_all if cn == :all
    YastCache.fetch(self, cn) {
      result = group_get( "system", cn )
      result = group_get( "local", cn )  if result.empty?
      return nil if result.empty?
      make_group result
    }
  end

  def self.find_all
    YastCache.fetch(self, :all) {
      result = groups_get "local"
      result.update( groups_get "system")
      result_array = []
      result.each { |k,v| result_array << make_group(v) }
      result_array.sort! {|x,y| x.cn <=> y.cn}
    }

YastCache.fetch(self, cn) and YastCache.fetch(self, :all) reads the groups cache with the id cn or :all. If it is empty the given code block will be executed and the evaluated value will be inserted into the cache and will be returned.

  def save
    existing_group = Group.group_get( group_type, old_cn )
    if existing_group.empty?
      result = YastService.Call( "YaPI::USERS::GroupAdd",
                                 { "type"      => ["s", group_type] },
                                 { "cn"        => ["s",cn], "userlist"  => ["as", members] } )
    else
      result = YastService.Call( "YaPI::USERS::GroupModify",
                                 { "type"      => ["s", group_type],
                                   "cn"        => ["s", old_cn]  },
                                 { "gidNumber" => ["i", gid],
                                   "cn"        => ["s",cn],
                                   "userlist"  => ["as", members] }
                               )
    end
    YastCache.reset(self, old_cn)
    result # result is empty string on success, error message otherwise
  end

With YastCache.reset(self, old_cn) the cache becomes invalid and will be filled up again in the background.

  def destroy
    existing_group = Group.group_get( group_type, old_cn )
    if existing_group.empty?
      ret = ""
    else
      ret = YastService.Call( "YaPI::USERS::GroupDelete", {"type" => ["s",group_type], "cn" => ["s",old_cn]})
    end
    YastCache.delete(self,old_cn)
    ret
  end

YastCache.delete(self,old_cn) deletes the cache for that group.