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

Side by Side Diff: chrome/common/extensions/docs/server2/handler.py

Issue 10829348: Extensions Docs Server: Large performance increase (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: server is fast Created 8 years, 4 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 | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 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 import logging 5 import logging
6 import os 6 import os
7 from StringIO import StringIO
7 import sys 8 import sys
8 9
9 from appengine_wrappers import webapp 10 from appengine_wrappers import webapp
10 from appengine_wrappers import memcache 11 from appengine_wrappers import memcache
11 from appengine_wrappers import urlfetch 12 from appengine_wrappers import urlfetch
12 13
13 from api_data_source import APIDataSource 14 from api_data_source import APIDataSource
14 from api_list_data_source import APIListDataSource 15 from api_list_data_source import APIListDataSource
15 from appengine_blobstore import AppEngineBlobstore 16 from appengine_blobstore import AppEngineBlobstore
16 from appengine_memcache import AppEngineMemcache 17 from appengine_memcache import AppEngineMemcache
17 from appengine_url_fetcher import AppEngineUrlFetcher 18 from appengine_url_fetcher import AppEngineUrlFetcher
18 from branch_utility import BranchUtility 19 from branch_utility import BranchUtility
19 from example_zipper import ExampleZipper 20 from example_zipper import ExampleZipper
20 from file_system_cache import FileSystemCache 21 from file_system_cache import FileSystemCache
22 import file_system_cache as fs_cache
21 from github_file_system import GithubFileSystem 23 from github_file_system import GithubFileSystem
22 from intro_data_source import IntroDataSource 24 from intro_data_source import IntroDataSource
23 from local_file_system import LocalFileSystem 25 from local_file_system import LocalFileSystem
24 from memcache_file_system import MemcacheFileSystem 26 from memcache_file_system import MemcacheFileSystem
25 from samples_data_source import SamplesDataSource 27 from samples_data_source import SamplesDataSource
26 from server_instance import ServerInstance 28 from server_instance import ServerInstance
27 from subversion_file_system import SubversionFileSystem 29 from subversion_file_system import SubversionFileSystem
28 from template_data_source import TemplateDataSource 30 from template_data_source import TemplateDataSource
29 import url_constants 31 import url_constants
30 32
31 # The branch that the server will default to when no branch is specified in the 33 # The branch that the server will default to when no branch is specified in the
32 # URL. This is necessary because it is not possible to pass flags to the script 34 # URL. This is necessary because it is not possible to pass flags to the script
33 # handler. 35 # handler.
34 DEFAULT_BRANCH = 'local' 36 DEFAULT_BRANCH = 'local'
35 37
36 BRANCH_UTILITY_MEMCACHE = AppEngineMemcache('branch_utility') 38 BRANCH_UTILITY_MEMCACHE = AppEngineMemcache('branch_utility')
37 BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL, 39 BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL,
38 DEFAULT_BRANCH, 40 DEFAULT_BRANCH,
39 AppEngineUrlFetcher(None), 41 AppEngineUrlFetcher(None),
40 BRANCH_UTILITY_MEMCACHE) 42 BRANCH_UTILITY_MEMCACHE)
41 43
44 GITHUB_MEMCACHE = AppEngineMemcache('github')
42 GITHUB_FILE_SYSTEM = GithubFileSystem( 45 GITHUB_FILE_SYSTEM = GithubFileSystem(
43 AppEngineUrlFetcher(url_constants.GITHUB_URL), 46 AppEngineUrlFetcher(url_constants.GITHUB_URL),
44 AppEngineMemcache('github'), 47 GITHUB_MEMCACHE,
45 AppEngineBlobstore()) 48 AppEngineBlobstore())
46 GITHUB_CACHE_BUILDER = FileSystemCache.Builder(GITHUB_FILE_SYSTEM) 49 GITHUB_CACHE_BUILDER = FileSystemCache.Builder(GITHUB_FILE_SYSTEM,
50 GITHUB_MEMCACHE)
47 51
48 STATIC_DIR_PREFIX = 'docs/server2' 52 STATIC_DIR_PREFIX = 'docs/server2'
49 EXTENSIONS_PATH = 'chrome/common/extensions' 53 EXTENSIONS_PATH = 'chrome/common/extensions'
50 DOCS_PATH = 'docs' 54 DOCS_PATH = 'docs'
51 API_PATH = 'api' 55 API_PATH = 'api'
52 INTRO_PATH = DOCS_PATH + '/server2/templates/intros' 56 TEMPLATE_PATH = DOCS_PATH + '/server2/templates'
53 ARTICLE_PATH = DOCS_PATH + '/server2/templates/articles' 57 INTRO_PATH = TEMPLATE_PATH + '/intros'
54 PUBLIC_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/public' 58 ARTICLE_PATH = TEMPLATE_PATH + '/articles'
55 PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private' 59 PUBLIC_TEMPLATE_PATH = TEMPLATE_PATH + '/public'
60 PRIVATE_TEMPLATE_PATH = TEMPLATE_PATH + '/private'
56 EXAMPLES_PATH = DOCS_PATH + '/examples' 61 EXAMPLES_PATH = DOCS_PATH + '/examples'
57 FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH
58 62
59 # Global cache of instances because Handler is recreated for every request. 63 # Global cache of instances because Handler is recreated for every request.
60 SERVER_INSTANCES = {} 64 SERVER_INSTANCES = {}
61 65
66 def _CreateMemcacheFileSystem(branch, branch_memcache):
67 svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH
68 stat_fetcher = AppEngineUrlFetcher(
69 svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL))
70 fetcher = AppEngineUrlFetcher(svn_url)
71 return MemcacheFileSystem(SubversionFileSystem(fetcher, stat_fetcher),
72 branch_memcache)
73
62 def _GetInstanceForBranch(channel_name, local_path): 74 def _GetInstanceForBranch(channel_name, local_path):
63 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name) 75 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name)
64 if branch in SERVER_INSTANCES: 76 if branch in SERVER_INSTANCES:
65 return SERVER_INSTANCES[branch] 77 return SERVER_INSTANCES[branch]
78 branch_memcache = AppEngineMemcache(branch)
66 if branch == 'local': 79 if branch == 'local':
67 file_system = LocalFileSystem(local_path) 80 file_system = LocalFileSystem(local_path)
68 else: 81 else:
69 svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH 82 file_system = _CreateMemcacheFileSystem(branch, branch_memcache)
70 stat_fetcher = AppEngineUrlFetcher(
71 svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL))
72 fetcher = AppEngineUrlFetcher(svn_url)
73 file_system = MemcacheFileSystem(
74 SubversionFileSystem(fetcher, stat_fetcher),
75 AppEngineMemcache(branch))
76 83
77 cache_builder = FileSystemCache.Builder(file_system) 84 cache_builder = FileSystemCache.Builder(file_system, branch_memcache)
78 api_list_data_source = APIListDataSource(cache_builder, 85 api_list_data_source_factory = APIListDataSource.Factory(cache_builder,
79 file_system, 86 file_system,
80 API_PATH, 87 API_PATH,
81 PUBLIC_TEMPLATE_PATH) 88 PUBLIC_TEMPLATE_PATH)
82 intro_data_source = IntroDataSource(cache_builder, 89 intro_data_source_factory = IntroDataSource.Factory(
83 [INTRO_PATH, ARTICLE_PATH]) 90 cache_builder,
91 [INTRO_PATH, ARTICLE_PATH])
84 samples_data_source_factory = SamplesDataSource.Factory(branch, 92 samples_data_source_factory = SamplesDataSource.Factory(branch,
85 file_system, 93 file_system,
86 GITHUB_FILE_SYSTEM, 94 GITHUB_FILE_SYSTEM,
87 cache_builder, 95 cache_builder,
88 GITHUB_CACHE_BUILDER, 96 GITHUB_CACHE_BUILDER,
89 EXAMPLES_PATH) 97 EXAMPLES_PATH)
90 api_data_source_factory = APIDataSource.Factory(cache_builder, 98 api_data_source_factory = APIDataSource.Factory(cache_builder,
91 API_PATH, 99 API_PATH,
92 samples_data_source_factory) 100 samples_data_source_factory)
93 template_data_source_factory = TemplateDataSource.Factory( 101 template_data_source_factory = TemplateDataSource.Factory(
94 channel_name, 102 channel_name,
95 api_data_source_factory, 103 api_data_source_factory,
96 api_list_data_source, 104 api_list_data_source_factory,
97 intro_data_source, 105 intro_data_source_factory,
98 samples_data_source_factory, 106 samples_data_source_factory,
99 cache_builder, 107 cache_builder,
100 PUBLIC_TEMPLATE_PATH, 108 PUBLIC_TEMPLATE_PATH,
101 PRIVATE_TEMPLATE_PATH) 109 PRIVATE_TEMPLATE_PATH)
102 example_zipper = ExampleZipper(file_system, 110 example_zipper = ExampleZipper(file_system,
103 cache_builder, 111 cache_builder,
104 DOCS_PATH, 112 DOCS_PATH,
105 EXAMPLES_PATH) 113 EXAMPLES_PATH)
106 SERVER_INSTANCES[branch] = ServerInstance( 114 SERVER_INSTANCES[branch] = ServerInstance(
107 template_data_source_factory, 115 template_data_source_factory,
108 example_zipper, 116 example_zipper,
109 cache_builder) 117 cache_builder)
110 return SERVER_INSTANCES[branch] 118 return SERVER_INSTANCES[branch]
111 119
112 def _GetURLFromBranch(branch): 120 def _GetURLFromBranch(branch):
113 if branch == 'trunk': 121 if branch == 'trunk':
114 return url_constants.SVN_TRUNK_URL + '/src' 122 return url_constants.SVN_TRUNK_URL + '/src'
115 return url_constants.SVN_BRANCH_URL + '/' + branch + '/src' 123 return url_constants.SVN_BRANCH_URL + '/' + branch + '/src'
116 124
117 def _CleanBranches(): 125 def _CleanBranches():
118 numbers = BRANCH_UTILITY.GetAllBranchNumbers() 126 numbers = BRANCH_UTILITY.GetAllBranchNumbers()
119 for key in SERVER_INSTANCES.keys(): 127 for key in SERVER_INSTANCES.keys():
120 if key not in numbers: 128 if key not in numbers:
121 SERVER_INSTANCES.pop(key) 129 SERVER_INSTANCES.pop(key)
122 130
131 class _MockResponse(object):
132 def __init__(self):
133 self.status = 200
134 self.out = StringIO()
135
136 def set_status(self, status):
137 self.status = status
138
139 class _MockRequest(object):
140 def __init__(self, path):
141 self.headers = {}
142 self.path = path
143
123 class Handler(webapp.RequestHandler): 144 class Handler(webapp.RequestHandler):
124 def __init__(self, request, response, local_path=EXTENSIONS_PATH): 145 def __init__(self, request, response, local_path=EXTENSIONS_PATH):
125 self._local_path = local_path 146 self._local_path = local_path
126 super(Handler, self).__init__(request, response) 147 super(Handler, self).__init__(request, response)
127 148
128 def _NavigateToPath(self, path): 149 def _NavigateToPath(self, path):
not at google - send to devlin 2012/08/20 05:27:10 this name always trips me up. Can we call it _Hand
cduvall 2012/08/20 21:28:09 Done.
129 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path) 150 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path)
130 # TODO: Detect that these are directories and serve index.html out of them. 151 # TODO: Detect that these are directories and serve index.html out of them.
131 if real_path.strip('/') == 'apps': 152 if real_path.strip('/') == 'apps':
132 real_path = 'apps/index.html' 153 real_path = 'apps/index.html'
133 if real_path.strip('/') == 'extensions': 154 if real_path.strip('/') == 'extensions':
134 real_path = 'extensions/index.html' 155 real_path = 'extensions/index.html'
135 _CleanBranches() 156 _CleanBranches()
136 _GetInstanceForBranch(channel_name, self._local_path).Get(real_path, 157 _GetInstanceForBranch(channel_name, self._local_path).Get(real_path,
137 self.request, 158 self.request,
138 self.response) 159 self.response)
139 160
161 def _Render(self, files, branch):
162 for f in files:
163 path = branch + f.split(PUBLIC_TEMPLATE_PATH)[-1]
164 self.request = _MockRequest(path)
165 self.response = _MockResponse()
166 self._NavigateToPath(path)
167
168 def _Cron(self, files):
169 self._file_system.Read(files).Get()
170
140 def get(self): 171 def get(self):
141 path = self.request.path 172 path = self.request.path
142 if '_ah/warmup' in path: 173 if path.startswith('/cron'):
not at google - send to devlin 2012/08/20 05:27:10 nit: can we split up these two distinct cases (cro
cduvall 2012/08/20 21:28:09 Done.
143 logging.info('Warmup request.') 174 branch = path.split('/')[-1]
144 self._NavigateToPath('trunk/extensions/samples.html') 175 logging.info('Running cron job for %s.' % branch)
145 self._NavigateToPath('dev/extensions/samples.html') 176 branch_memcache = AppEngineMemcache(branch)
146 self._NavigateToPath('beta/extensions/samples.html') 177 self._file_system = _CreateMemcacheFileSystem(branch, branch_memcache)
not at google - send to devlin 2012/08/20 05:27:10 file_system doesn't need to be on self, we can def
cduvall 2012/08/20 21:28:09 Done.
147 self._NavigateToPath('stable/extensions/samples.html') 178 builder = FileSystemCache.Builder(self._file_system, branch_memcache)
148 # Only do this request if we are on the deployed server. 179 cache = builder.build(self._Cron, fs_cache.FS_CACHE_CRON)
149 # Bug: http://crbug.com/141910 180 render_cache = builder.build(lambda x: self._Render(x, branch),
150 if DEFAULT_BRANCH != 'local': 181 fs_cache.FS_CACHE_RENDER)
151 self._NavigateToPath('apps/samples.html') 182 cache.GetFromFileListing(TEMPLATE_PATH)
183 cache.GetFromFileListing(API_PATH)
184 cache.GetFromFileListing(EXAMPLES_PATH)
185 render_cache.GetFromFileListing(PUBLIC_TEMPLATE_PATH)
not at google - send to devlin 2012/08/20 05:27:10 why does rendering all files from the public templ
cduvall 2012/08/20 21:28:09 I just thought it would be a little faster getting
not at google - send to devlin 2012/08/21 00:30:11 Interesting. Did you find that to be the case? If
cduvall 2012/08/21 01:33:33 I don't think it makes too much of a difference, s
152 return 186 return
153 187
154 # Redirect paths like "directory" to "directory/". This is so relative file 188 # Redirect paths like "directory" to "directory/". This is so relative file
155 # paths will know to treat this as a directory. 189 # paths will know to treat this as a directory.
156 if os.path.splitext(path)[1] == '' and path[-1] != '/': 190 if os.path.splitext(path)[1] == '' and path[-1] != '/':
157 self.redirect(path + '/') 191 self.redirect(path + '/')
158 path = path.replace('/chrome/', '') 192 path = path.replace('/chrome/', '')
159 path = path.strip('/') 193 path = path.strip('/')
160 self._NavigateToPath(path) 194 self._NavigateToPath(path)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698