Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library pub_server.copy_and_write_repository; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 import 'package:logging/logging.dart'; | |
| 10 import 'package:pub_server/repository.dart'; | |
| 11 | |
| 12 final Logger _logger = new Logger('pub_server.cow_repository'); | |
| 13 | |
| 14 /// A [CopyAndWriteRepository] writes to one repository and directs | |
| 15 /// read-misses to another repository. | |
| 16 /// | |
| 17 /// Package versions not available from the read-write repository will be | |
| 18 /// fetched from a read-fallback repository and uploaded to the read-write | |
| 19 /// repository. | |
| 20 /// | |
|
Søren Gjesse
2015/02/16 10:23:54
Please describe that this will also cache packages
kustermann
2015/02/16 11:42:10
Done.
| |
| 21 /// New package versions which get uploaded will be stored only locally. | |
| 22 class CopyAndWriteRepository extends PackageRepository { | |
| 23 final PackageRepository local; | |
| 24 final PackageRepository remote; | |
| 25 final _RemoteMetadataCache _localCache; | |
| 26 final _RemoteMetadataCache _remoteCache; | |
| 27 | |
| 28 /// Construct a new proxy with [local] as the local [PackageRepository] which | |
| 29 /// is used for uploading new package versions to and [remote] as the | |
| 30 /// read-only [PackageRepository] which is consulted on misses in [local]. | |
| 31 CopyAndWriteRepository(PackageRepository local, PackageRepository remote) | |
| 32 : this.local = local, | |
| 33 this.remote = remote, | |
| 34 this._localCache = new _RemoteMetadataCache(local), | |
| 35 this._remoteCache = new _RemoteMetadataCache(remote); | |
| 36 | |
| 37 Stream<PackageVersion> versions(String package) { | |
| 38 var controller; | |
| 39 | |
| 40 onListen() { | |
| 41 Future.wait([_localCache.fetchVersionlist(package), | |
| 42 _remoteCache.fetchVersionlist(package)]).then((tuple) { | |
| 43 var versions = new Set() | |
| 44 ..addAll(tuple[0]) | |
| 45 ..addAll(tuple[1]); | |
| 46 for (var version in versions) controller.add(version); | |
| 47 controller.close(); | |
| 48 }); | |
| 49 } | |
| 50 | |
| 51 controller = new StreamController(onListen: onListen); | |
| 52 return controller.stream; | |
| 53 } | |
| 54 | |
| 55 Future<PackageVersion> lookupVersion(String package, String version) { | |
| 56 return versions(package) | |
| 57 .where((pv) => pv.versionString == version) | |
| 58 .toList().then((List<PackageVersion> versions) { | |
| 59 if (versions.length >= 1) return versions.first; | |
| 60 return null; | |
| 61 }); | |
| 62 } | |
| 63 | |
| 64 Future<Stream> download(String package, String version) { | |
|
Søren Gjesse
2015/02/16 10:23:54
You are already using async and await in other par
kustermann
2015/02/16 11:42:10
It was very old code. - Done
| |
| 65 return local.lookupVersion(package, version).then((packageVersion) { | |
| 66 if (packageVersion != null) { | |
| 67 _logger.info('Serving $package/$version from local repository.'); | |
| 68 return local.download(package, packageVersion.versionString); | |
| 69 } else { | |
| 70 // We first download the package from the remote repository and store | |
| 71 // it locally. Then we read the local version and return it. | |
| 72 _logger.info('Downloading $package/$version from remote repository.'); | |
| 73 return remote.download(package, version).then((stream) { | |
| 74 _logger.info('Upload $package/$version to local repository.'); | |
| 75 return local.upload(stream).then((_) { | |
| 76 _logger.info('Serving $package/$version from local repository.'); | |
| 77 return local.download(package, version); | |
| 78 }); | |
| 79 }); | |
| 80 } | |
| 81 }); | |
| 82 } | |
| 83 | |
| 84 bool get supportsUpload => true; | |
| 85 | |
| 86 Future upload(Stream<List<int>> data) { | |
| 87 _logger.info('Starting upload to local package repository.'); | |
|
Søren Gjesse
2015/02/16 10:23:54
Should it be allowed to upload packages which are
kustermann
2015/02/16 11:42:10
Exactly. We're also not ensuring that we cannot ov
| |
| 88 return local.upload(data).then((_) { | |
| 89 _logger.info('Upload finished.'); | |
| 90 // TODO: It's not really necessary to invalidate all. | |
| 91 _localCache.invalidateAll(); | |
| 92 }); | |
| 93 } | |
| 94 | |
| 95 bool get supportsAsyncUpload => false; | |
| 96 } | |
| 97 | |
| 98 /// A cache for [PackageVersion] objects for a given `package`. | |
| 99 /// | |
| 100 /// The constructor takes a [PackageRepository] which will be used to populate | |
| 101 /// the cache. | |
| 102 class _RemoteMetadataCache { | |
| 103 final PackageRepository remote; | |
| 104 | |
| 105 Map<String, Set<PackageVersion>> _versions = {}; | |
| 106 Map<String, Completer<Set<PackageVersion>>> _versionCompleter = {}; | |
|
Søren Gjesse
2015/02/16 10:23:54
_versionCompleters (add the plural s)
kustermann
2015/02/16 11:42:10
Done.
| |
| 107 | |
| 108 _RemoteMetadataCache(this.remote); | |
| 109 | |
| 110 // TODO: After a cache expiration we should invalidate entries and re-fetch | |
|
Søren Gjesse
2015/02/16 10:23:54
What should the expiration time be? We have no way
kustermann
2015/02/16 11:42:10
Well, the local cache could have the following pol
| |
| 111 // them. | |
| 112 Future<List<PackageVersion>> fetchVersionlist(String package) { | |
| 113 return _versionCompleter.putIfAbsent(package, () { | |
| 114 var c = new Completer(); | |
| 115 | |
| 116 _versions.putIfAbsent(package, () => new Set()); | |
| 117 remote.versions(package).toList().then((versions) { | |
| 118 _versions[package].addAll(versions); | |
| 119 c.complete(_versions[package]); | |
| 120 }); | |
| 121 | |
| 122 return c; | |
| 123 }).future.then((set) => set.toList()); | |
| 124 } | |
| 125 | |
| 126 void addVersion(String package, PackageVersion version) { | |
| 127 _versions.putIfAbsent(version.packageName, () => new Set()).add(version); | |
| 128 } | |
| 129 | |
| 130 void invalidateAll() { | |
| 131 _versionCompleter.clear(); | |
| 132 _versions.clear(); | |
| 133 } | |
| 134 } | |
| OLD | NEW |