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

Unified Diff: bin/src/examples/cow_repository.dart

Issue 930513002: Added README, a bin/serve example server which can be used locally. (Closed) Base URL: https://github.com/dart-lang/pub_server.git@master
Patch Set: Created 5 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: bin/src/examples/cow_repository.dart
diff --git a/bin/src/examples/cow_repository.dart b/bin/src/examples/cow_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..101103855751e3d3863679881cbd2747222ec803
--- /dev/null
+++ b/bin/src/examples/cow_repository.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_server.copy_and_write_repository;
+
+import 'dart:async';
+
+import 'package:logging/logging.dart';
+import 'package:pub_server/repository.dart';
+
+final Logger _logger = new Logger('pub_server.cow_repository');
+
+/// A [CopyAndWriteRepository] writes to one repository and directs
+/// read-misses to another repository.
+///
+/// Package versions not available from the read-write repository will be
+/// fetched from a read-fallback repository and uploaded to the read-write
+/// repository.
+///
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.
+/// New package versions which get uploaded will be stored only locally.
+class CopyAndWriteRepository extends PackageRepository {
+ final PackageRepository local;
+ final PackageRepository remote;
+ final _RemoteMetadataCache _localCache;
+ final _RemoteMetadataCache _remoteCache;
+
+ /// Construct a new proxy with [local] as the local [PackageRepository] which
+ /// is used for uploading new package versions to and [remote] as the
+ /// read-only [PackageRepository] which is consulted on misses in [local].
+ CopyAndWriteRepository(PackageRepository local, PackageRepository remote)
+ : this.local = local,
+ this.remote = remote,
+ this._localCache = new _RemoteMetadataCache(local),
+ this._remoteCache = new _RemoteMetadataCache(remote);
+
+ Stream<PackageVersion> versions(String package) {
+ var controller;
+
+ onListen() {
+ Future.wait([_localCache.fetchVersionlist(package),
+ _remoteCache.fetchVersionlist(package)]).then((tuple) {
+ var versions = new Set()
+ ..addAll(tuple[0])
+ ..addAll(tuple[1]);
+ for (var version in versions) controller.add(version);
+ controller.close();
+ });
+ }
+
+ controller = new StreamController(onListen: onListen);
+ return controller.stream;
+ }
+
+ Future<PackageVersion> lookupVersion(String package, String version) {
+ return versions(package)
+ .where((pv) => pv.versionString == version)
+ .toList().then((List<PackageVersion> versions) {
+ if (versions.length >= 1) return versions.first;
+ return null;
+ });
+ }
+
+ 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
+ return local.lookupVersion(package, version).then((packageVersion) {
+ if (packageVersion != null) {
+ _logger.info('Serving $package/$version from local repository.');
+ return local.download(package, packageVersion.versionString);
+ } else {
+ // We first download the package from the remote repository and store
+ // it locally. Then we read the local version and return it.
+ _logger.info('Downloading $package/$version from remote repository.');
+ return remote.download(package, version).then((stream) {
+ _logger.info('Upload $package/$version to local repository.');
+ return local.upload(stream).then((_) {
+ _logger.info('Serving $package/$version from local repository.');
+ return local.download(package, version);
+ });
+ });
+ }
+ });
+ }
+
+ bool get supportsUpload => true;
+
+ Future upload(Stream<List<int>> data) {
+ _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
+ return local.upload(data).then((_) {
+ _logger.info('Upload finished.');
+ // TODO: It's not really necessary to invalidate all.
+ _localCache.invalidateAll();
+ });
+ }
+
+ bool get supportsAsyncUpload => false;
+}
+
+/// A cache for [PackageVersion] objects for a given `package`.
+///
+/// The constructor takes a [PackageRepository] which will be used to populate
+/// the cache.
+class _RemoteMetadataCache {
+ final PackageRepository remote;
+
+ Map<String, Set<PackageVersion>> _versions = {};
+ 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.
+
+ _RemoteMetadataCache(this.remote);
+
+ // 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
+ // them.
+ Future<List<PackageVersion>> fetchVersionlist(String package) {
+ return _versionCompleter.putIfAbsent(package, () {
+ var c = new Completer();
+
+ _versions.putIfAbsent(package, () => new Set());
+ remote.versions(package).toList().then((versions) {
+ _versions[package].addAll(versions);
+ c.complete(_versions[package]);
+ });
+
+ return c;
+ }).future.then((set) => set.toList());
+ }
+
+ void addVersion(String package, PackageVersion version) {
+ _versions.putIfAbsent(version.packageName, () => new Set()).add(version);
+ }
+
+ void invalidateAll() {
+ _versionCompleter.clear();
+ _versions.clear();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698