Welcome!
Everything is fine.

Requests, Threads and Flask

Plan

  1. Announcements
  2. Discuss Ajax assignment
  3. Recap Requests
  4. Recap Threads
  5. Quiz Questions
  6. Breakout session activities (Ajax)

Announcements

  • I would like to meet with entire teams on Wednesdays; please attend

Requests

The client might not just be a browser. Most of the time it will be, but it might not. The requests module allows us to use a Python program to do so. You will not need to write such a module in this course. I'm teaching it because I hope:

  • It will clarify what's going on with the Ajax assignment, by allow you to focus on the back end.
  • It will broaden your understanding of web database applications.
  • It will help you in internships, research projects, and the like.

The essentials are pretty easy:

The requests module allows us

  • to make GET requests
  • to find out the response status code
  • to get the returned web page
  • to get the data in JSON format
  • to make POST requests
  • to use sessions by saving a cookie and sending it in a later request

Examples:

resp = requests.get(url)
print(resp.status_code) 
page = resp.text
data = resp.json()
resp = requests.post(url, data=payload_dictionary)
session = resp.cookies
resp = requests.get(url, cookies=session)

I forgot to send a quiz on requests; what are your questions?

Demo from last week. Here's the server:

cd ~/cs304/likes/
source ~/cs304/venv/bin/activate
python app.py 

And the client

cd ~/cs304/likes/
source ~/cs304/venv/bin/activate
python req.py 
I'll answer your [requests questions](../../quizzes/quiz22b.html)

Threads

What are the important points for today?

  • Threading is an important concept and strategy
  • Flask code has to be thread-safe, also called re-entrant.
  • Local variables and parameters are always thread-safe.
  • Instance variables, class variables, and global variables may not be thread-safe (but they might be).
  • Some uses of shared variables can be thread-safe; others require locks
  • Nevertheless, threads and shared variables can be useful.

Thread Concepts

  • Threads are (relatively) cheap to create.
  • Threads share code and heap, but have their own stack.
  • Inter-thread communication is easy via shared variables and data structures. This can be a big advantage.
  • However, modifications of shared data structures might need to be atomic, requiring locks or other synchronization
A process with two threads that share code and heap, but have their own stack.
A process with two threads that share code and heap, but have their own stack.

A multi-threaded Flask app:

multi-threaded Flask with two threads

So, our two threads are sharing the code and heap from our Flask app.

Race to 50

Here's the race to 50 and race to 50 fixed examples:

python raceto50.py
python raceto50fixed.py
diff raceto50.py raceto50fixed.py

Practical Project Implications

  1. Avoid global variables. Global constants are okay.
  2. If you need a global variable, make sure your code is thread-safe.
  3. Make sure every request uses a separate connection
  4. However, each request can share a single connection
  5. Bask in the knowledge that you're writing multi-threaded code. 😁

You probably won't need locking. But you should know about it just in case.

Questions

I'll answer your quiz questions.

Configuring Apache

FYI, here's how Apache can be configured. This is pretty standard.

# worker MPM 
# StartServers: initial number of server processes to start 
# MaxClients: maximum number of simultaneous client connections 
# MinSpareThreads: minimum number of worker threads which are kept spare 
# MaxSpareThreads: maximum number of worker threads which are kept spare 
# ThreadsPerChild: constant number of worker threads in each server process 
# MaxRequestsPerChild: maximum number of requests a server process serves 
<IfModule worker.c>
StartServers         4 
MaxClients         300 
MinSpareThreads     25 
MaxSpareThreads     75
ThreadsPerChild     25 
MaxRequestsPerChild  0 
</IfModule>

Activity

  • Work on what you need to work on

Summary

  • Threads are cool and powerful, but you have to be careful.
  • Don't put stuff in a shared variable that belongs just to one request
  • lock or synchronize access to shared data.
  • Each request should have its own database connection, to avoid getting wires crossed.

Group Work

  • I'll display the results/charts
  • While things aren't perfect (they never are), they're mostly pretty good.
    • Lookup/CRUD biggest issue is finding time. I tried to pair people that had time in common, but ...
    • Project work is mostly going well
  • Advice
    • Remember that your team is counting on you.
    • Ask for help (from teammates, from me) if you need it.
    • Communicate
    • If you are frustrated with a teammate, you can talk to me. Don't suffer in silence.

Polling Example

I have a cool activity in which you would build a "poll" or "voting" app that kept the counters in memory instead of needing a database at all. But access to those counters probably ought to be synchronized.

The activity is in pub/downloads/threads:

cd ~/cs304/
source ~/cs304/venv/bin/activate
cp -r ~cs304/pub/downloads/threads/ threads
cd threads/

I'll set up a SSH tunnel, run the app and demo:

python voteLock.py

Actually, I have an improved version as vl2.py:

python vl2.py

We'll vote on a simple resolution.

Here's the crucial part of the old code:

@app.route('/vote/', methods=["GET", "POST"])
def vote():
    global resolutionText, numYes, numNo
    text = resolutionText
    state = 'none' if (text is None) else 'voting' 
    # result is only used when voting stops
    result = ''                                    
    if request.method == 'POST':
        try:
            action = request.form.get('submit')

            if action == START:
                voteLock.acquire()
                if resolutionText is None:
                    resolutionText = request.form.get('resolution')
                    text = resolutionText
                    numYes = 0
                    numNo = 0
                    state = 'voting'
                    msg = ('started resolution: '+text)
                else:
                    msg = ('''error: can't start resolution;
                              one is in progress: '''
                           +resolutionText)
                voteLock.release()
                flash(msg)

            elif action == AYE or action == NAY:
                voteLock.acquire()
                if resolutionText is None:
                    text = ''
                    msg = ('no vote is in progress')
                else:
                    text = resolutionText
                    if action == AYE:
                        msg = ('you voted YES on '+text)
                        numYes += 1
                    else:
                        msg = ('you voted NO on '+text)
                        numNo += 1
                voteLock.release()
                flash(msg)

            elif action == STOP:
                voteLock.acquire()
                text = resolutionText # copy the text
                if resolutionText is None:
                    # someone else ended the vote
                    tmpl = '''vote already ended;
                              motion unknown:
                              {yeas} yes votes
                              and {nays} no votes'''
                elif numYes > numNo:
                    tmpl = '{motion} passes: {yeas} > {nays}'
                elif numNo > numYes:
                    tmpl = '{motion} fails: {nays} > {yeas}'
                else:
                    tmpl = '''{motion} tied! {nays} == {yeas}.
                              Broward County begins recount ....'''
                resolutionText = None
                voteLock.release()
                state = 'done'
                result = tmpl.format(motion=text,
                                     yeas=numYes,
                                     nays=numNo)
                flash(result)
            else:

So, if we know about Locks and such, we can have apps that don't need databases and can keep data in memory!