Repository: mindsdb/mindsdb

Issue:
Async download Scout on start
When mindsdb-http api starts, it checks new version of gui is available. If it available, mindsdb starts download it. Issue here - is mindsdb-http server is not start until gui-downloading finishes. Because of this launch time of http-server can be long.
To fix it, need run initialize_static (https://github.com/mindsdb/mindsdb/blob/staging/mindsdb/api/http/start.py#L24) function in separate process/thread.
Also, it will be good to make concurrent download of GUI files here: https://github.com/mindsdb/mindsdb/blob/staging/mindsdb/api/http/initialize.py#L141


Discussion:

Resolving patch:
diff --git a/mindsdb/api/http/initialize.py b/mindsdb/api/http/initialize.py
--- a/mindsdb/api/http/initialize.py
+++ b/mindsdb/api/http/initialize.py
@@ -8,6 +8,7 @@
 from pathlib import Path
 import logging
 import traceback
+import concurrent.futures
 
 from flask import Flask, url_for
 from flask_restx import Api
@@ -138,11 +139,23 @@ def initialize_static(config):
             }
         ]
 
-        for r in resources:
-            response = requests.get(r['url'])
-            if response.status_code != 200:
-                raise Exception(f"Error {response.status_code} GET {r['url']}")
-            open(r['path'], 'wb').write(response.content)
+        def get_resources(resource):
+            try:
+                response = requests.get(resource['url'])
+                if response.status_code != requests.status_codes.codes.ok:
+                    return Exception(f"Error {response.status_code} GET {resource['url']}")
+                open(resource['path'], 'wb').write(response.content)
+            except Exception as e:
+                return e
+            return None
+
+        # to make downloading faster download each resource in a separate thread
+        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
+            future_to_url = {executor.submit(get_resources, r): r for r in resources}
+            for future in concurrent.futures.as_completed(future_to_url):
+                res = future.result()
+                if res is not None:
+                    raise res
 
     except Exception as e:
         log.error(f'Error during downloading files from s3: {e}')
diff --git a/mindsdb/api/http/start.py b/mindsdb/api/http/start.py
--- a/mindsdb/api/http/start.py
+++ b/mindsdb/api/http/start.py
@@ -1,6 +1,7 @@
 import os
 import logging
 import multiprocessing
+import threading
 from pathlib import Path
 
 from werkzeug.exceptions import HTTPException
@@ -23,7 +24,9 @@ def start(config, verbose=False):
 
     initialize_log(config, 'http', wrap_print=True)
 
-    initialize_static(config)
+    # start static initialization in a separate thread
+    init_static_thread = threading.Thread(target=initialize_static, args=(config,))
+    init_static_thread.start()
 
     app, api = initialize_flask(config)
     initialize_interfaces(config, app)
@@ -58,6 +61,8 @@ def handle_exception(e):
 
     server = os.environ.get('MINDSDB_DEFAULT_SERVER', 'waitress')
 
+    # waiting static initialization
+    init_static_thread.join()
     if server.lower() == 'waitress':
         serve(app, port=port, host=host)
     elif server.lower() == 'flask':


Test patch: