• Show log

    Commit

  • Hash : 2ba7020f
    Author : Patrick Steinhardt
    Date : 2019-06-27T09:23:59

    config_file: avoid re-reading files on write
    
    When we rewrite the configuration file due to any of its values
    being modified, we call `config_refresh` to update the in-memory
    representation of our config file backend. This is needlessly
    wasteful though, as `config_refresh` will always open the on-disk
    representation to reads the file contents while we already know
    the complete file contents at this point in time as we have just
    written it to disk.
    
    Implement a new function `config_refresh_from_buffer` that will
    refresh the backend's config entries from a buffer instead of
    from the config file itself. Note that this will thus _not_
    update the backend's timestamp, which will cause us to re-read
    the buffer when performing a read operation on it. But this is
    still an improvement as we now lazily re-read the contents, and
    most importantly we will avoid constantly re-reading the contents
    if we perform multiple write operations.
    
    The following strace demonstrates this if we're re-writing a key
    multiple times. It uses our config example with `config_set`
    changed to update the file 10 times with different keys:
    
    	$ strace lg2 config x.x z |& grep '^open.*config'
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    
    And now with the optimization of `config_refresh_from_buffer`:
    
    	$ strace lg2 config x.x z |& grep '^open.*config'
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    	open("/tmp/repo/.git/config.lock", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
    	open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 4
    
    As can be seen, this is quite a lot of `open` calls less.