Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change the webserver to initalize the database lazily #675

Open
wants to merge 1 commit into
base: random-forest-mode-detection
Choose a base branch
from

Conversation

shankari
Copy link
Contributor

I can technically merge this to master, although I am not sure whether I should :)

Testing done:

Started webserver. No message about loading database.

(emission) C02KT61MFFT0:e-mission-server shankari$ ./e-mission-py.bash emission/net/api/cfc_webapp.py
analysis.debug.conf.json not configured, falling back to sample, default configuration
Finished configuring logging for <RootLogger root (WARNING)>
Replaced json_dumps in plugin with the one from bson
Changing bt.json_loads from <function <lambda> at 0x10deb3d90> to <function loads at 0x10e7320d0>
Running with HTTPS turned OFF - use a reverse proxy on production
Bottle v0.13-dev server starting up (using CherootServer())...
Listening on http://0.0.0.0:8080/
Hit Ctrl-C to quit.

Accessed webserver through a browser. We connect to the database only then.

START 2019-04-25 16:54:45.175715 GET /
END 2019-04-25 16:54:45.181657 GET /  0.005769968032836914
Connecting to database URL localhost

Artificially introduced an error (changed the if (_current_db is None) to if (_current_db is not None). Database starts up fine but we get an (as expected)
error while serving files because we are unable to contact the database.

Running with HTTPS turned OFF - use a reverse proxy on production
Bottle v0.13-dev server starting up (using CherootServer())...
Listening on http://0.0.0.0:8080/
Hit Ctrl-C to quit.

START 2019-04-25 17:02:11.109216 GET /
END 2019-04-25 17:02:11.120386 GET /  0.011026859283447266
Traceback (most recent call last):
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 1012, in _handle
    self.trigger_hook('after_request')
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 706, in trigger_hook
    return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 706, in <listcomp>
    return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
  File "emission/net/api/cfc_webapp.py", line 425, in after_request
    msTimeNow, duration)
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/stats.py", line 13, in store_server_api_time
    esds.store_server_api_time(user_id, call, ts, reading)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/decorations/stats_queries.py", line 20, in store_server_api_time
    store_stats_entry(user_id, "stats/server_api_time", call, ts, reading)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/decorations/stats_queries.py", line 47, in store_stats_entry
    return esta.TimeSeries.get_time_series(user_id).insert(new_entry)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/timeseries/abstract_timeseries.py", line 20, in get_time_series
    return bits.BuiltinTimeSeries(user_id)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/timeseries/builtin_timeseries.py", line 37, in __init__
    self.timeseries_db = get_ts_enum_map()[esta.EntryType.DATA_TYPE]
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/timeseries/builtin_timeseries.py", line 24, in get_ts_enum_map
    esta.EntryType.DATA_TYPE: edb.get_timeseries_db(),
  File "/Users/shankari/e-mission/e-mission-server/emission/core/get_database.py", line 160, in get_timeseries_db
    TimeSeries = _get_current_db().Stage_timeseries
AttributeError: 'NoneType' object has no attribute 'Stage_timeseries'

I can technically merge this to master, although I am not sure whether I should :)

Testing done:

Started webserver. No message about loading database.

```
(emission) C02KT61MFFT0:e-mission-server shankari$ ./e-mission-py.bash emission/net/api/cfc_webapp.py
analysis.debug.conf.json not configured, falling back to sample, default configuration
Finished configuring logging for <RootLogger root (WARNING)>
Replaced json_dumps in plugin with the one from bson
Changing bt.json_loads from <function <lambda> at 0x10deb3d90> to <function loads at 0x10e7320d0>
Running with HTTPS turned OFF - use a reverse proxy on production
Bottle v0.13-dev server starting up (using CherootServer())...
Listening on http://0.0.0.0:8080/
Hit Ctrl-C to quit.
```

Accessed webserver through a browser. We connect to the database only then.

```
START 2019-04-25 16:54:45.175715 GET /
END 2019-04-25 16:54:45.181657 GET /  0.005769968032836914
Connecting to database URL localhost
```

Artificially introduced an error (changed the `if (_current_db is None)` to `if
(_current_db is not None)`. Database starts up fine but we get an (as expected)
error while serving files because we are unable to contact the database.

```
Running with HTTPS turned OFF - use a reverse proxy on production
Bottle v0.13-dev server starting up (using CherootServer())...
Listening on http://0.0.0.0:8080/
Hit Ctrl-C to quit.

START 2019-04-25 17:02:11.109216 GET /
END 2019-04-25 17:02:11.120386 GET /  0.011026859283447266
Traceback (most recent call last):
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 1012, in _handle
    self.trigger_hook('after_request')
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 706, in trigger_hook
    return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 706, in <listcomp>
    return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
  File "emission/net/api/cfc_webapp.py", line 425, in after_request
    msTimeNow, duration)
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/stats.py", line 13, in store_server_api_time
    esds.store_server_api_time(user_id, call, ts, reading)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/decorations/stats_queries.py", line 20, in store_server_api_time
    store_stats_entry(user_id, "stats/server_api_time", call, ts, reading)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/decorations/stats_queries.py", line 47, in store_stats_entry
    return esta.TimeSeries.get_time_series(user_id).insert(new_entry)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/timeseries/abstract_timeseries.py", line 20, in get_time_series
    return bits.BuiltinTimeSeries(user_id)
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/timeseries/builtin_timeseries.py", line 37, in __init__
    self.timeseries_db = get_ts_enum_map()[esta.EntryType.DATA_TYPE]
  File "/Users/shankari/e-mission/e-mission-server/emission/storage/timeseries/builtin_timeseries.py", line 24, in get_ts_enum_map
    esta.EntryType.DATA_TYPE: edb.get_timeseries_db(),
  File "/Users/shankari/e-mission/e-mission-server/emission/core/get_database.py", line 160, in get_timeseries_db
    TimeSeries = _get_current_db().Stage_timeseries
AttributeError: 'NoneType' object has no attribute 'Stage_timeseries'
```
@shankari
Copy link
Contributor Author

With this patch, the server will not connect to the database on start, but will still do so on any API call since we store the API time in the database. I have not included the change to remove that as part of this PR since I might actually merge this PR to master and I don't want to merge that yet. If this is the new architecture, we should really maintain state on the client (e.g. is_connected) and only log server stats to the database if we are connected. But that is a bigger change than I want to make now

@shankari
Copy link
Contributor Author

This is the additional change required to turn off saving stats on API calls.

diff --git a/emission/net/api/cfc_webapp.py b/emission/net/api/cfc_webapp.py
index 93e69cf0..9faf4815 100644
--- a/emission/net/api/cfc_webapp.py
+++ b/emission/net/api/cfc_webapp.py
@@ -415,16 +415,16 @@ def after_request():
   new_duration = request.params.timer.elapsed
   if round(old_div((duration - new_duration), new_duration) > 100) > 0:
     logging.error("old style duration %s != timer based duration %s" % (duration, new_duration))
-    stats.store_server_api_error(request.params.user_uuid, "MISMATCH_%s_%s" %
-                                 (request.method, request.path), msTimeNow, duration - new_duration)
+    # stats.store_server_api_error(request.params.user_uuid, "MISMATCH_%s_%s" %
+    #                              (request.method, request.path), msTimeNow, duration - new_duration)

   print("END %s %s %s %s %s " % (datetime.now(), request.method, request.path, request.params.user_uuid, duration))
   logging.debug("END %s %s %s %s " % (request.method, request.path, request.params.user_uuid, duration))
   # Keep track of the time and duration for each call
-  stats.store_server_api_time(request.params.user_uuid, "%s_%s" % (request.method, request.path),
-        msTimeNow, duration)
-  stats.store_server_api_time(request.params.user_uuid, "%s_%s_cputime" % (request.method, request.path),
-        msTimeNow, new_duration)
+  # stats.store_server_api_time(request.params.user_uuid, "%s_%s" % (request.method, request.path),
+  #       msTimeNow, duration)
+  # stats.store_server_api_time(request.params.user_uuid, "%s_%s_cputime" % (request.method, request.path),
+  #       msTimeNow, new_duration)

 # Auth helpers BEGIN
 # This should only be used by createUserProfile since we may not have a UUID

remove_after_hook.patch.gz

@shankari
Copy link
Contributor Author

If you apply the change from this PR, and the manual patch in #675 (comment), then the database will not be accessed until we get an API call that needs to read/write data.

Testing done:

  • Artificially introduce an error (changed the if (_current_db is None) to if (_current_db is not None)
  • Connected using the browser, pages loaded with no errors
  • Went to the metrics tab
  • Clicked on "Get"
  • Only now did we get an error
START 2019-04-25 17:06:28.294446 GET /img/personal/list_view.png
END 2019-04-25 17:06:28.295493 GET /img/personal/list_view.png  0.0009567737579345703
START 2019-04-25 17:06:28.296489 GET /img/personal/detail_view.png
START 2019-04-25 17:06:28.297447 GET /img/personal/common_trips.png
END 2019-04-25 17:06:28.298847 GET /img/personal/detail_view.png  0.0022420883178710938
END 2019-04-25 17:06:28.299705 GET /img/personal/common_trips.png  0.0021851062774658203
START 2019-04-25 17:06:28.310609 GET /img/aggregate/bike_march_2016.png
END 2019-04-25 17:06:28.312278 GET /img/aggregate/bike_march_2016.png  0.0015270709991455078
START 2019-04-25 17:06:28.313627 GET /img/aggregate/bike_apr_2016.png
END 2019-04-25 17:06:28.316041 GET /img/aggregate/bike_apr_2016.png  0.0017452239990234375

START 2019-04-25 17:06:46.313896 POST /result/metrics/local_date
END 2019-04-25 17:06:46.315118 POST /result/metrics/local_date  0.0011289119720458984
Traceback (most recent call last):
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 997, in _handle
    out = route.call(**args)
  File "/Users/shankari/e-mission/e-mission-server/emission/net/api/bottle.py", line 1998, in wrapper
    rv = callback(*a, **ka)
...

@AmplabJenkins
Copy link

Merged build finished. Test PASSed.

@AmplabJenkins
Copy link

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://amplab.cs.berkeley.edu/jenkins//job/e-mission-server-prb/1102/
Test PASSed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants