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

Side by Side 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 unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698