mirror of
				https://github.com/ohwgiles/laminar.git
				synced 2025-06-13 12:54:29 +00:00 
			
		
		
		
	improve frontend performance with large logs
frontend would furiously try to render as fast as it received data chunks from the server. This causes a lot of extra load on the browser renderer if the logs are large. Buffer and batch calls to rerender to improve performance. resolves #165
This commit is contained in:
		
							parent
							
								
									bb087b72ee
								
							
						
					
					
						commit
						efafda16ff
					
				| @ -187,8 +187,8 @@ | |||||||
|    </div> |    </div> | ||||||
|   </div> |   </div> | ||||||
|   <div class="console-log"> |   <div class="console-log"> | ||||||
|    <code v-html="log"></code> |    <code></code> | ||||||
|    <span v-show="job.result == 'running'" v-html="runIcon('running')" style="display: block;"></span> |    <span v-show="!logComplete" v-html="runIcon('running')" style="display: block;"></span> | ||||||
|   </div> |   </div> | ||||||
|  </div></template> |  </div></template> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -643,7 +643,7 @@ const Run = templateId => { | |||||||
|   const state = { |   const state = { | ||||||
|     job: { artifacts: [], upstream: {} }, |     job: { artifacts: [], upstream: {} }, | ||||||
|     latestNum: null, |     latestNum: null, | ||||||
|     log: '', |     logComplete: false, | ||||||
|   }; |   }; | ||||||
|   const logFetcher = (vm, name, num) => { |   const logFetcher = (vm, name, num) => { | ||||||
|     const abort = new AbortController(); |     const abort = new AbortController(); | ||||||
| @ -651,27 +651,52 @@ const Run = templateId => { | |||||||
|       // ATOW pipeThrough not supported in Firefox
 |       // ATOW pipeThrough not supported in Firefox
 | ||||||
|       //const reader = res.body.pipeThrough(new TextDecoderStream).getReader();
 |       //const reader = res.body.pipeThrough(new TextDecoderStream).getReader();
 | ||||||
|       const reader = res.body.getReader(); |       const reader = res.body.getReader(); | ||||||
|  |       const target = document.getElementsByTagName('code')[0]; | ||||||
|  |       let logToRender = ''; | ||||||
|  |       let logComplete = false; | ||||||
|  |       let tid = null; | ||||||
|  | 
 | ||||||
|  |       function updateUI() { | ||||||
|  |         // output may contain private ANSI CSI escape sequence to point to
 | ||||||
|  |         // downstream jobs. ansi_up (correctly) discards unknown sequences,
 | ||||||
|  |         // so they must be matched before passing through ansi_up. ansi_up
 | ||||||
|  |         // also (correctly) escapes HTML, so they need to be converted back
 | ||||||
|  |         // to links after going through ansi_up.
 | ||||||
|  |         // A better solution one day would be if ansi_up were to provide
 | ||||||
|  |         // a callback interface for handling unknown sequences.
 | ||||||
|  |         // Also, update the DOM directly rather than using a binding through
 | ||||||
|  |         // Vue, the performance is noticeably better with large logs.
 | ||||||
|  |         target.innerHTML += ansi_up.ansi_to_html( | ||||||
|  |           logToRender.replace(/\033\[\{([^:]+):(\d+)\033\\/g, (m, $1, $2) => | ||||||
|  |             '~~~~LAMINAR_RUN~'+$1+':'+$2+'~' | ||||||
|  |           ) | ||||||
|  |         ).replace(/~~~~LAMINAR_RUN~([^:]+):(\d+)~/g, (m, $1, $2) => | ||||||
|  |           '<a href="jobs/'+$1+'" onclick="return LaminarApp.navigate(this.href);">'+$1+'</a>:'+ | ||||||
|  |           '<a href="jobs/'+$1+'/'+$2+'" onclick="return LaminarApp.navigate(this.href);">#'+$2+'</a>' | ||||||
|  |         ); | ||||||
|  |         logToRender = ''; | ||||||
|  |         if (logComplete) { | ||||||
|  |           // output finished
 | ||||||
|  |           state.logComplete = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       return function pump() { |       return function pump() { | ||||||
|         return reader.read().then(({done, value}) => { |         return reader.read().then(({done, value}) => { | ||||||
|           value = utf8decoder.decode(value); |           if (done) { | ||||||
|           if (done) |             // do not set state.logComplete directly, because rendering
 | ||||||
|  |             // may take some time, and we don't want the progress indicator
 | ||||||
|  |             // to disappear before rendering is complete. Instead, delay
 | ||||||
|  |             // it until after the log has been added to the DOM
 | ||||||
|  |             logComplete = true; | ||||||
|             return; |             return; | ||||||
|           // output may contain private ANSI CSI escape sequence to point to
 |           } | ||||||
|           // downstream jobs. ansi_up (correctly) discards unknown sequences,
 |           logToRender += utf8decoder.decode(value); | ||||||
|           // so they must be matched before passing through ansi_up. ansi_up
 |           // sometimes logs can be very large, and we are calling pump()
 | ||||||
|           // also (correctly) escapes HTML, so they need to be converted back
 |           // furiously to get all the data to the client. To prevent straining
 | ||||||
|           // to links after going through ansi_up.
 |           // the client renderer, buffer the data and delay the UI updates.
 | ||||||
|           // A better solution one day would be if ansi_up were to provide
 |           clearTimeout(tid); | ||||||
|           // a callback interface for handling unknown sequences.
 |           tid = setTimeout(updateUI, 125); | ||||||
|           state.log += ansi_up.ansi_to_html( |  | ||||||
|             value.replace(/\033\[\{([^:]+):(\d+)\033\\/g, (m, $1, $2) => |  | ||||||
|               '~~~~LAMINAR_RUN~'+$1+':'+$2+'~' |  | ||||||
|             ) |  | ||||||
|           ).replace(/~~~~LAMINAR_RUN~([^:]+):(\d+)~/g, (m, $1, $2) => |  | ||||||
|             '<a href="jobs/'+$1+'" onclick="return LaminarApp.navigate(this.href);">'+$1+'</a>:'+ |  | ||||||
|             '<a href="jobs/'+$1+'/'+$2+'" onclick="return LaminarApp.navigate(this.href);">#'+$2+'</a>' |  | ||||||
|           ); |  | ||||||
|           vm.$forceUpdate(); |  | ||||||
|           return pump(); |           return pump(); | ||||||
|         }); |         }); | ||||||
|       }(); |       }(); | ||||||
| @ -694,7 +719,9 @@ const Run = templateId => { | |||||||
|         state.job = data; |         state.job = data; | ||||||
|         state.latestNum = data.latestNum; |         state.latestNum = data.latestNum; | ||||||
|         state.jobsRunning = [data]; |         state.jobsRunning = [data]; | ||||||
|         state.log = ''; |         state.logComplete = false; | ||||||
|  |         // DOM is used directly for performance
 | ||||||
|  |         document.getElementsByTagName('code')[0].innerHTML = ''; | ||||||
|         if(this.logstream) |         if(this.logstream) | ||||||
|           this.logstream.abort(); |           this.logstream.abort(); | ||||||
|         if(data.started) |         if(data.started) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user