Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(938)

Side by Side Diff: client/cipd/client.go

Issue 1870263002: cipd: instance cache (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@master
Patch Set: cipd: instance cache Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Package cipd implements client side of Chrome Infra Package Deployer. 5 // Package cipd implements client side of Chrome Infra Package Deployer.
6 // 6 //
7 // Binary package file format (in free form representation): 7 // Binary package file format (in free form representation):
8 // <binary package> := <zipped data> 8 // <binary package> := <zipped data>
9 // <zipped data> := DeterministicZip(<all input files> + <manifest json>) 9 // <zipped data> := DeterministicZip(<all input files> + <manifest json>)
10 // <manifest json> := File{ 10 // <manifest json> := File{
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 // instance sorted by tag key and creation timestamp (newest first). If 'tags' 265 // instance sorted by tag key and creation timestamp (newest first). If 'tags'
266 // is empty, fetches all attached tags, otherwise only ones specified. 266 // is empty, fetches all attached tags, otherwise only ones specified.
267 FetchInstanceTags(pin common.Pin, tags []string) ([]TagInfo, error) 267 FetchInstanceTags(pin common.Pin, tags []string) ([]TagInfo, error)
268 268
269 // FetchInstanceRefs returns information about refs pointing to the pack age 269 // FetchInstanceRefs returns information about refs pointing to the pack age
270 // instance sorted by modification timestamp (newest first). If 'ref' is 270 // instance sorted by modification timestamp (newest first). If 'ref' is
271 // empty, fetches all refs, otherwise only ones specified. 271 // empty, fetches all refs, otherwise only ones specified.
272 FetchInstanceRefs(pin common.Pin, refs []string) ([]RefInfo, error) 272 FetchInstanceRefs(pin common.Pin, refs []string) ([]RefInfo, error)
273 273
274 // FetchInstance downloads package instance file from the repository. 274 // FetchInstance downloads package instance file from the repository.
275 » FetchInstance(pin common.Pin, output io.WriteSeeker) error 275 » FetchInstance(pin common.Pin, output io.ReadWriteSeeker) error
276 276
277 // FetchAndDeployInstance fetches the package instance and deploys it in to 277 // FetchAndDeployInstance fetches the package instance and deploys it in to
278 // the site root. It doesn't check whether the instance is already deplo yed. 278 // the site root. It doesn't check whether the instance is already deplo yed.
279 FetchAndDeployInstance(pin common.Pin) error 279 FetchAndDeployInstance(pin common.Pin) error
280 280
281 // ListPackages returns a list of strings of package names. 281 // ListPackages returns a list of strings of package names.
282 ListPackages(path string, recursive bool) ([]string, error) 282 ListPackages(path string, recursive bool) ([]string, error)
283 283
284 // SearchInstances finds all instances with given tag and optionally nam e and 284 // SearchInstances finds all instances with given tag and optionally nam e and
285 // returns their concrete Pins. 285 // returns their concrete Pins.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 // RPC requests. 329 // RPC requests.
330 AuthenticatedClientFactory HTTPClientFactory 330 AuthenticatedClientFactory HTTPClientFactory
331 331
332 // AnonymousClientFactory lazily creates http.Client to use for making 332 // AnonymousClientFactory lazily creates http.Client to use for making
333 // requests to storage. 333 // requests to storage.
334 AnonymousClientFactory HTTPClientFactory 334 AnonymousClientFactory HTTPClientFactory
335 335
336 // UserAgent is put into User-Agent HTTP header with each request. 336 // UserAgent is put into User-Agent HTTP header with each request.
337 UserAgent string 337 UserAgent string
338 338
339 » // CacheDir is a directory for shared cache. If empty, tags are cached 339 » // CacheDir is a directory for shared cache. If empty, instances are not
340 » // inside the site root. If both Root and CacheDir are empty, tag cache 340 » // cached and tags are cached inside the site root. If both Root and
341 » // is disabled. 341 » // CacheDir are empty, tag cache is disabled.
342 CacheDir string 342 CacheDir string
343 } 343 }
344 344
345 // NewClient initializes CIPD client object. 345 // NewClient initializes CIPD client object.
346 func NewClient(opts ClientOptions) Client { 346 func NewClient(opts ClientOptions) Client {
347 if opts.ServiceURL == "" { 347 if opts.ServiceURL == "" {
348 opts.ServiceURL = ServiceURL 348 opts.ServiceURL = ServiceURL
349 } 349 }
350 if opts.Logger == nil { 350 if opts.Logger == nil {
351 opts.Logger = logging.Null() 351 opts.Logger = logging.Null()
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 storage storage 386 storage storage
387 387
388 // deployer knows how to install packages to local file system. Thread s afe. 388 // deployer knows how to install packages to local file system. Thread s afe.
389 deployer local.Deployer 389 deployer local.Deployer
390 390
391 // tagCache is used to cache (pkgname, tag) -> instanceID mapping. 391 // tagCache is used to cache (pkgname, tag) -> instanceID mapping.
392 // Thread safe, but lazily initialized under lock. 392 // Thread safe, but lazily initialized under lock.
393 tagCache *internal.TagCache 393 tagCache *internal.TagCache
394 tagCacheInit sync.Once 394 tagCacheInit sync.Once
395 395
396 // instanceCache is a file-system based cache of instances.
397 instanceCache *internal.InstanceCache
398 instanceCacheInit sync.Once
399
396 // authClient is a lazily created http.Client to use for authenticated 400 // authClient is a lazily created http.Client to use for authenticated
397 // requests. Thread safe, but lazily initialized under lock. 401 // requests. Thread safe, but lazily initialized under lock.
398 authClient *http.Client 402 authClient *http.Client
399 403
400 // anonClient is a lazily created http.Client to use for anonymous reque sts. 404 // anonClient is a lazily created http.Client to use for anonymous reque sts.
401 // Thread safe, but lazily initialized under lock. 405 // Thread safe, but lazily initialized under lock.
402 anonClient *http.Client 406 anonClient *http.Client
403 } 407 }
404 408
405 // doAuthenticatedHTTPRequest is used by remote implementation to make HTTP call s. 409 // doAuthenticatedHTTPRequest is used by remote implementation to make HTTP call s.
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 out, err := client.tagCache.Save() 483 out, err := client.tagCache.Save()
480 if err == nil { 484 if err == nil {
481 err = fs.EnsureFile(path, out, 0666) 485 err = fs.EnsureFile(path, out, 0666)
482 } 486 }
483 if err != nil { 487 if err != nil {
484 client.Logger.Warningf("cipd: failed to update tag cache - %s", err) 488 client.Logger.Warningf("cipd: failed to update tag cache - %s", err)
485 } 489 }
486 client.tagCache = nil 490 client.tagCache = nil
487 } 491 }
488 492
493 // instanceCachePath returns path to the instance cache directory or "" if
494 // instance cache is disabled.
495 func (client *clientImpl) instanceCachePath() string {
496 if client.CacheDir == "" {
497 return ""
498 }
499 return filepath.Join(client.CacheDir, "instances")
500 }
501
502 // getInstanceCache lazy-initializes instanceCache and returns it.
503 func (client *clientImpl) getInstanceCache() *internal.InstanceCache {
504 client.instanceCacheInit.Do(func() {
505 if path := client.instanceCachePath(); path != "" {
506 cachePath := local.NewFileSystem(path, client.Logger)
507 client.instanceCache = internal.LoadInstanceCache(cacheP ath, client.Logger, client.clock.now())
508 }
509 })
510 return client.instanceCache
511 }
512
513 // closeInstanceCache dumps any changes made to instance cache to disk, if neces sary.
514 func (client *clientImpl) closeInstanceCache() {
515 if client.instanceCache == nil || !client.instanceCache.Dirty() {
516 return
517 }
518
519 if err := client.instanceCache.Save(); err != nil {
520 client.Logger.Warningf("cipd: failed to save instance cache - %s ", err)
521 }
522 client.instanceCache = nil
523 }
524
489 func (client *clientImpl) FetchACL(packagePath string) ([]PackageACL, error) { 525 func (client *clientImpl) FetchACL(packagePath string) ([]PackageACL, error) {
490 return client.remote.fetchACL(packagePath) 526 return client.remote.fetchACL(packagePath)
491 } 527 }
492 528
493 func (client *clientImpl) ModifyACL(packagePath string, changes []PackageACLChan ge) error { 529 func (client *clientImpl) ModifyACL(packagePath string, changes []PackageACLChan ge) error {
494 return client.remote.modifyACL(packagePath, changes) 530 return client.remote.modifyACL(packagePath, changes)
495 } 531 }
496 532
497 func (client *clientImpl) ListPackages(path string, recursive bool) ([]string, e rror) { 533 func (client *clientImpl) ListPackages(path string, recursive bool) ([]string, e rror) {
498 pkgs, dirs, err := client.remote.listPackages(path, recursive) 534 pkgs, dirs, err := client.remote.listPackages(path, recursive)
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after
741 } 777 }
742 778
743 func (client *clientImpl) FetchInstanceRefs(pin common.Pin, refs []string) ([]Re fInfo, error) { 779 func (client *clientImpl) FetchInstanceRefs(pin common.Pin, refs []string) ([]Re fInfo, error) {
744 err := common.ValidatePin(pin) 780 err := common.ValidatePin(pin)
745 if err != nil { 781 if err != nil {
746 return nil, err 782 return nil, err
747 } 783 }
748 return client.remote.fetchRefs(pin, refs) 784 return client.remote.fetchRefs(pin, refs)
749 } 785 }
750 786
751 func (client *clientImpl) FetchInstance(pin common.Pin, output io.WriteSeeker) e rror { 787 func (client *clientImpl) FetchInstance(pin common.Pin, output io.ReadWriteSeeke r) error {
752 err := common.ValidatePin(pin) 788 err := common.ValidatePin(pin)
753 if err != nil { 789 if err != nil {
754 return err 790 return err
755 } 791 }
792
793 now := client.clock.now()
794
795 cache := client.getInstanceCache()
796 if cache != nil {
797 switch err := cache.Get(pin, output, now); {
798 case os.IsNotExist(err):
799 // output is not corrupted.
800
801 case err != nil:
802 client.Logger.Warningf("cipd: could not get %s from cach e - %s", pin, err)
803 // Output may be corrupted. Rewinding back.
804 if _, err := output.Seek(0, 0); err != nil {
Vadim Sh. 2016/04/11 16:52:02 well, it may not be enough. We also need to trunca
nodir 2016/04/11 22:16:16 Done.
805 return err
806 }
807
808 default:
809 client.Logger.Infof("cipd: cache hit for %s", pin)
810 return nil
811 }
812 }
813
756 client.Logger.Infof("cipd: resolving fetch URL for %s", pin) 814 client.Logger.Infof("cipd: resolving fetch URL for %s", pin)
757 fetchInfo, err := client.remote.fetchInstance(pin) 815 fetchInfo, err := client.remote.fetchInstance(pin)
758 » if err == nil { 816 » if err != nil {
759 » » err = client.storage.download(fetchInfo.fetchURL, output) 817 » » goto fetchFail
760 } 818 }
761 » if err != nil { 819
762 » » client.Logger.Errorf("cipd: failed to fetch %s - %s", pin, err) 820 » if err = client.storage.download(fetchInfo.fetchURL, output); err != nil {
763 » » return err 821 » » goto fetchFail
764 } 822 }
823
765 client.Logger.Infof("cipd: successfully fetched %s", pin) 824 client.Logger.Infof("cipd: successfully fetched %s", pin)
825
826 if cache != nil {
827 if _, err := output.Seek(0, 0); err != nil {
Vadim Sh. 2016/04/11 16:52:02 It may be simpler to always download into cache, a
nodir 2016/04/11 22:16:16 Done.
828 client.Logger.Warningf("cipd: could not seek output to t he beginning for caching - %s", err)
829 } else if err := cache.Put(pin, output, now); err != nil {
830 client.Logger.Warningf("cipd: could not cache %s - %s", pin, err)
831 }
832 }
833
766 return nil 834 return nil
835
836 fetchFail:
837 client.Logger.Errorf("cipd: failed to fetch %s - %s", pin, err)
838 return err
767 } 839 }
768 840
769 func (client *clientImpl) FetchAndDeployInstance(pin common.Pin) error { 841 func (client *clientImpl) FetchAndDeployInstance(pin common.Pin) error {
770 err := common.ValidatePin(pin) 842 err := common.ValidatePin(pin)
771 if err != nil { 843 if err != nil {
772 return err 844 return err
773 } 845 }
774 846
775 // Use temp file for storing package file. Delete it when done. 847 // Use temp file for storing package file. Delete it when done.
776 var instance local.PackageInstance 848 var instance local.PackageInstance
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
941 client.Logger.Infof("All changes applied.") 1013 client.Logger.Infof("All changes applied.")
942 return actions, nil 1014 return actions, nil
943 } 1015 }
944 return actions, ErrEnsurePackagesFailed 1016 return actions, ErrEnsurePackagesFailed
945 } 1017 }
946 1018
947 func (client *clientImpl) Close() { 1019 func (client *clientImpl) Close() {
948 client.lock.Lock() 1020 client.lock.Lock()
949 defer client.lock.Unlock() 1021 defer client.lock.Unlock()
950 client.closeTagCache() 1022 client.closeTagCache()
1023 client.closeInstanceCache()
951 client.authClient = nil 1024 client.authClient = nil
952 client.anonClient = nil 1025 client.anonClient = nil
953 } 1026 }
954 1027
955 //////////////////////////////////////////////////////////////////////////////// 1028 ////////////////////////////////////////////////////////////////////////////////
956 // Private structs and interfaces. 1029 // Private structs and interfaces.
957 1030
958 type clock interface { 1031 type clock interface {
959 now() time.Time 1032 now() time.Time
960 sleep(time.Duration) 1033 sleep(time.Duration)
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
1032 } 1105 }
1033 1106
1034 // buildInstanceIDMap builds mapping {package name -> instance ID}. 1107 // buildInstanceIDMap builds mapping {package name -> instance ID}.
1035 func buildInstanceIDMap(pins []common.Pin) map[string]string { 1108 func buildInstanceIDMap(pins []common.Pin) map[string]string {
1036 out := map[string]string{} 1109 out := map[string]string{}
1037 for _, p := range pins { 1110 for _, p := range pins {
1038 out[p.PackageName] = p.InstanceID 1111 out[p.PackageName] = p.InstanceID
1039 } 1112 }
1040 return out 1113 return out
1041 } 1114 }
OLDNEW
« no previous file with comments | « no previous file | client/cipd/internal/instancecache.go » ('j') | client/cipd/internal/instancecache.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698