3
\$\begingroup\$

I'm sort of at a loss for what to do with webapps and whether what I'm doing is actually allowed or not. Here is a sample of the type of thing I'm doing at the moment. I need a list that is accessible by essentially everything.

global_list = []

@bottle.route('/')
def index():
    global_list.append(4)

@bottle.route('/test')
def test():
    return len(global_list)

@bottle.run(port='8080')

The problem (or what I think is a problem) is that it feels wrong and I've been told it is unsafe to use a global variable. This code works perfectly fine, but is there a better or safer way to handle this? Even with multiple clients at the same time, the data in the list currently does not get corrupted. What kind of issues am I going to run into if I keep using this?

\$\endgroup\$

closed as off-topic by Billal Begueradj, Graipher, Ludisposed, Stephen Rauch, Dannnno Jun 20 '18 at 13:54

This question appears to be off-topic. The users who voted to close gave these specific reasons:

  • "Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site." – Billal Begueradj, Graipher, Dannnno
  • "Code not implemented or not working as intended: Code Review is a community where programmers peer-review your working code to address issues such as security, maintainability, performance, and scalability. We require that the code be working correctly, to the best of the author's knowledge, before proceeding with a review." – Ludisposed, Stephen Rauch
If this question can be reworded to fit the rules in the help center, please edit the question.

2
\$\begingroup\$

Ah, the age old pragmatism vs. purism. While it sounds like you have a healthy amount of skepticism, I think this is actually one of the few instances where it's OK to use a global variable. The core of your problem is you need to share data (state) between two routes. A global variable is the simplest way of doing this if all you're doing is appending the number 4 each time.

Here's some things to consider:

  • Security: You mentioned security. Will any sensitive information be stored in global_list? If so, you should consider putting safegards in place (authentication and authorization) to make sure only the right people see the appropriate information. Also, if you store user input to the variable, be very careful how you use the data (SQL injection).
  • Persistence: Will state need to be maintained across webserver restarts? If everything is in a Python variable, it will be lost when you shut down the process. You may want to persist the data to a filesystem or database.
  • Resources: Are you going to be storing lots of information in this global variable? If so, the process may run out of memory, because the global variable is never garbage collected. The solution here is to store the data somewhere and generate aggregate data to use in the app instead (or do pagination).
  • Shared-state side effects: Anytime you share data, you have to be conscious of side effects caused by altering this data. If two routes are reading/writing from the same variable, one route could do something that breaks the other route.

More on resources:

For instance, if you store 1KB every time the / route is loaded and get 1M visitors a month, you'd be up to 1GB of memory just for global_list in around 1 month. 1KB is a lot of data, and you probably don't need all of this information to do your calculation in /test. Instead, you could aggregate the data by using global_list as a running total for the full set of data stored elsewhere (a database or filesystem).

If you're doing more than simply presenting a count of records in /test, and you need whole (or partial) records, then you can do pagination. Let's say you're presenting a list of the records in global_list. In this case, /test could simply return the first 10 records by default, but allow the user to see more pages by providing a page parameter. So if the user wants to see records 31-40, they could request /test?page=4

\$\endgroup\$
  • \$\begingroup\$ Security: No sensitive Data. Persistence: no need to maintain state. A restart rebuilds the variable from DB. Resources: A valid concern. Could you explain more about pagination/aggregate data? Shared-state: There is only one route that modifies the variable, all others are just consumers. Thanks a lot! \$\endgroup\$ – Tadgh May 15 '13 at 13:53
  • \$\begingroup\$ Makes sense. This is a pesonal web app for distributed personal use that will have at most 2-3 connections at once, and the data resets on shutdown, so this won't be an issue. Thanks for the feedback \$\endgroup\$ – Tadgh May 15 '13 at 15:00
1
\$\begingroup\$

What about threading? You run just one thread of the server? Anyway, you should take a look at memcached for bottle (https://pypi.python.org/pypi/bottle-memcache/). Maybe not for this particular case but it can come in handy for bigger projects.

\$\endgroup\$
  • \$\begingroup\$ Threading shouldnt be an issue here, as the application is for a single user, maybe two at a time. It won't be hitting the web. If you are curious, it's a drink-mixing machine that is for single-person use: i.imgur.com/1ge8vq5.png . Memcached looks pretty awesome though. Thanks! \$\endgroup\$ – Tadgh May 21 '13 at 10:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.