Mercurial > piecrust2
comparison piecrust/baking/worker.py @ 451:838f3964f400
bake: Optimize the bake by not using custom classes for passing info.
See previous changeset about pickling performance between processes. Now
just use plain standard structures, or the new `fastpickle` when needed.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 06 Jul 2015 21:30:49 -0700 |
parents | aefe70229fdd |
children | 8351a77e13f5 |
comparison
equal
deleted
inserted
replaced
450:298f8f46432a | 451:838f3964f400 |
---|---|
47 self.job_handlers = job_handlers | 47 self.job_handlers = job_handlers |
48 | 48 |
49 app.env.stepTimerSince("BakeWorkerInit", self.work_start_time) | 49 app.env.stepTimerSince("BakeWorkerInit", self.work_start_time) |
50 | 50 |
51 def process(self, job): | 51 def process(self, job): |
52 handler = self.job_handlers[job.job_type] | 52 handler = self.job_handlers[job['type']] |
53 with self.app.env.timerScope(type(handler).__name__): | 53 with self.app.env.timerScope(type(handler).__name__): |
54 return handler.handleJob(job) | 54 return handler.handleJob(job['job']) |
55 | 55 |
56 def getReport(self): | 56 def getReport(self): |
57 self.app.env.stepTimerSince("BakeWorker_%d_Total" % self.wid, | 57 self.app.env.stepTimerSince("BakeWorker_%d_Total" % self.wid, |
58 self.work_start_time) | 58 self.work_start_time) |
59 return { | 59 return { |
60 'type': 'timers', | 60 'type': 'timers', |
61 'data': self.app.env._timers} | 61 'data': self.app.env._timers} |
62 | 62 |
63 | 63 |
64 JOB_LOAD, JOB_RENDER_FIRST, JOB_BAKE = range(0, 3) | 64 JOB_LOAD, JOB_RENDER_FIRST, JOB_BAKE = range(0, 3) |
65 | |
66 | |
67 class BakeWorkerJob(object): | |
68 def __init__(self, job_type, payload): | |
69 self.job_type = job_type | |
70 self.payload = payload | |
71 | 65 |
72 | 66 |
73 class JobHandler(object): | 67 class JobHandler(object): |
74 def __init__(self, app, ctx): | 68 def __init__(self, app, ctx): |
75 self.app = app | 69 self.app = app |
85 errors.append(str(ex)) | 79 errors.append(str(ex)) |
86 ex = ex.__cause__ | 80 ex = ex.__cause__ |
87 return errors | 81 return errors |
88 | 82 |
89 | 83 |
90 class PageFactoryInfo(object): | 84 def save_factory(fac): |
91 def __init__(self, fac): | 85 return { |
92 self.source_name = fac.source.name | 86 'source_name': fac.source.name, |
93 self.rel_path = fac.rel_path | 87 'rel_path': fac.rel_path, |
94 self.metadata = fac.metadata | 88 'metadata': fac.metadata} |
95 | |
96 def build(self, app): | |
97 source = app.getSource(self.source_name) | |
98 return PageFactory(source, self.rel_path, self.metadata) | |
99 | 89 |
100 | 90 |
101 class LoadJobPayload(object): | 91 def load_factory(app, info): |
102 def __init__(self, fac): | 92 source = app.getSource(info['source_name']) |
103 self.factory_info = PageFactoryInfo(fac) | 93 return PageFactory(source, info['rel_path'], info['metadata']) |
104 | |
105 | |
106 class LoadJobResult(object): | |
107 def __init__(self, source_name, path): | |
108 self.source_name = source_name | |
109 self.path = path | |
110 self.config = None | |
111 self.errors = None | |
112 | |
113 | |
114 class RenderFirstSubJobPayload(object): | |
115 def __init__(self, fac): | |
116 self.factory_info = PageFactoryInfo(fac) | |
117 | |
118 | |
119 class RenderFirstSubJobResult(object): | |
120 def __init__(self, path): | |
121 self.path = path | |
122 self.errors = None | |
123 | |
124 | |
125 class BakeJobPayload(object): | |
126 def __init__(self, fac, route_metadata, previous_entry, | |
127 dirty_source_names, tax_info=None): | |
128 self.factory_info = PageFactoryInfo(fac) | |
129 self.route_metadata = route_metadata | |
130 self.previous_entry = previous_entry | |
131 self.dirty_source_names = dirty_source_names | |
132 self.taxonomy_info = tax_info | |
133 | |
134 | |
135 class BakeJobResult(object): | |
136 def __init__(self, path, tax_info=None): | |
137 self.path = path | |
138 self.taxonomy_info = tax_info | |
139 self.sub_entries = None | |
140 self.errors = None | |
141 | 94 |
142 | 95 |
143 class LoadJobHandler(JobHandler): | 96 class LoadJobHandler(JobHandler): |
144 def handleJob(self, job): | 97 def handleJob(self, job): |
145 # Just make sure the page has been cached. | 98 # Just make sure the page has been cached. |
146 fac = job.payload.factory_info.build(self.app) | 99 fac = load_factory(self.app, job) |
147 logger.debug("Loading page: %s" % fac.ref_spec) | 100 logger.debug("Loading page: %s" % fac.ref_spec) |
148 result = LoadJobResult(fac.source.name, fac.path) | 101 result = { |
102 'source_name': fac.source.name, | |
103 'path': fac.path, | |
104 'config': None, | |
105 'errors': None} | |
149 try: | 106 try: |
150 page = fac.buildPage() | 107 page = fac.buildPage() |
151 page._load() | 108 page._load() |
152 result.config = page.config.getAll() | 109 result['config'] = page.config.getAll() |
153 except Exception as ex: | 110 except Exception as ex: |
154 logger.debug("Got loading error. Sending it to master.") | 111 logger.debug("Got loading error. Sending it to master.") |
155 result.errors = _get_errors(ex) | 112 result['errors'] = _get_errors(ex) |
156 if self.ctx.debug: | 113 if self.ctx.debug: |
157 logger.exception(ex) | 114 logger.exception(ex) |
158 return result | 115 return result |
159 | 116 |
160 | 117 |
161 class RenderFirstSubJobHandler(JobHandler): | 118 class RenderFirstSubJobHandler(JobHandler): |
162 def handleJob(self, job): | 119 def handleJob(self, job): |
163 # Render the segments for the first sub-page of this page. | 120 # Render the segments for the first sub-page of this page. |
164 fac = job.payload.factory_info.build(self.app) | 121 fac = load_factory(self.app, job) |
165 | 122 |
166 # These things should be OK as they're checked upstream by the baker. | 123 # These things should be OK as they're checked upstream by the baker. |
167 route = self.app.getRoute(fac.source.name, fac.metadata, | 124 route = self.app.getRoute(fac.source.name, fac.metadata, |
168 skip_taxonomies=True) | 125 skip_taxonomies=True) |
169 assert route is not None | 126 assert route is not None |
171 page = fac.buildPage() | 128 page = fac.buildPage() |
172 route_metadata = create_route_metadata(page) | 129 route_metadata = create_route_metadata(page) |
173 qp = QualifiedPage(page, route, route_metadata) | 130 qp = QualifiedPage(page, route, route_metadata) |
174 ctx = PageRenderingContext(qp) | 131 ctx = PageRenderingContext(qp) |
175 | 132 |
176 result = RenderFirstSubJobResult(fac.path) | 133 result = { |
134 'path': fac.path, | |
135 'errors': None} | |
177 logger.debug("Preparing page: %s" % fac.ref_spec) | 136 logger.debug("Preparing page: %s" % fac.ref_spec) |
178 try: | 137 try: |
179 render_page_segments(ctx) | 138 render_page_segments(ctx) |
180 except Exception as ex: | 139 except Exception as ex: |
181 logger.debug("Got rendering error. Sending it to master.") | 140 logger.debug("Got rendering error. Sending it to master.") |
182 result.errors = _get_errors(ex) | 141 result['errors'] = _get_errors(ex) |
183 if self.ctx.debug: | 142 if self.ctx.debug: |
184 logger.exception(ex) | 143 logger.exception(ex) |
185 return result | 144 return result |
186 | 145 |
187 | 146 |
190 super(BakeJobHandler, self).__init__(app, ctx) | 149 super(BakeJobHandler, self).__init__(app, ctx) |
191 self.page_baker = PageBaker(app, ctx.out_dir, ctx.force) | 150 self.page_baker = PageBaker(app, ctx.out_dir, ctx.force) |
192 | 151 |
193 def handleJob(self, job): | 152 def handleJob(self, job): |
194 # Actually bake the page and all its sub-pages to the output folder. | 153 # Actually bake the page and all its sub-pages to the output folder. |
195 fac = job.payload.factory_info.build(self.app) | 154 fac = load_factory(self.app, job['factory_info']) |
196 | 155 |
197 route_metadata = job.payload.route_metadata | 156 route_metadata = job['route_metadata'] |
198 tax_info = job.payload.taxonomy_info | 157 tax_info = job['taxonomy_info'] |
199 if tax_info is not None: | 158 if tax_info is not None: |
200 route = self.app.getTaxonomyRoute(tax_info.taxonomy_name, | 159 route = self.app.getTaxonomyRoute(tax_info.taxonomy_name, |
201 tax_info.source_name) | 160 tax_info.source_name) |
202 else: | 161 else: |
203 route = self.app.getRoute(fac.source.name, route_metadata, | 162 route = self.app.getRoute(fac.source.name, route_metadata, |
205 assert route is not None | 164 assert route is not None |
206 | 165 |
207 page = fac.buildPage() | 166 page = fac.buildPage() |
208 qp = QualifiedPage(page, route, route_metadata) | 167 qp = QualifiedPage(page, route, route_metadata) |
209 | 168 |
210 result = BakeJobResult(fac.path, tax_info) | 169 result = { |
211 previous_entry = job.payload.previous_entry | 170 'path': fac.path, |
212 dirty_source_names = job.payload.dirty_source_names | 171 'taxonomy_info': tax_info, |
172 'sub_entries': None, | |
173 'errors': None} | |
174 previous_entry = job['prev_entry'] | |
175 dirty_source_names = job['dirty_source_names'] | |
213 logger.debug("Baking page: %s" % fac.ref_spec) | 176 logger.debug("Baking page: %s" % fac.ref_spec) |
214 try: | 177 try: |
215 sub_entries = self.page_baker.bake( | 178 sub_entries = self.page_baker.bake( |
216 qp, previous_entry, dirty_source_names, tax_info) | 179 qp, previous_entry, dirty_source_names, tax_info) |
217 result.sub_entries = sub_entries | 180 result['sub_entries'] = sub_entries |
218 | 181 |
219 except BakingError as ex: | 182 except BakingError as ex: |
220 logger.debug("Got baking error. Sending it to master.") | 183 logger.debug("Got baking error. Sending it to master.") |
221 result.errors = _get_errors(ex) | 184 result['errors'] = _get_errors(ex) |
222 if self.ctx.debug: | 185 if self.ctx.debug: |
223 logger.exception(ex) | 186 logger.exception(ex) |
224 | 187 |
225 return result | 188 return result |
226 | 189 |