foundationdb/class-scheduling.html

751 lines
88 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<title>Class Scheduling &#8212; FoundationDB ON documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Class Scheduling in Ruby" href="class-scheduling-ruby.html" />
<link rel="prev" title="Tutorials" href="tutorials.html" />
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1'>
<meta name="apple-mobile-web-app-capable" content="yes">
<script type="text/javascript" src="_static/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="_static/js/jquery-fix.js"></script>
<script type="text/javascript" src="_static/bootstrap-3.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="_static/bootstrap-sphinx.js"></script>
</head><body>
<div id="navbar" class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<!-- .btn-navbar is used as the toggle for collapsed navbar content -->
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">
FoundationDB</a>
<span class="navbar-text navbar-version pull-left"><b>7.3.57</b></span>
</div>
<div class="collapse navbar-collapse nav-collapse">
<ul class="nav navbar-nav">
<li><a href="contents.html">Site Map</a></li>
<li class="dropdown globaltoc-container">
<a role="button"
id="dLabelGlobalToc"
data-toggle="dropdown"
data-target="#"
href="index.html">Site <b class="caret"></b></a>
<ul class="dropdown-menu globaltoc"
role="menu"
aria-labelledby="dLabelGlobalToc"><ul class="current">
<li class="toctree-l1"><a class="reference internal" href="local-dev.html">Local Development</a></li>
<li class="toctree-l1"><a class="reference internal" href="internal-dev-tools.html">Internal Dev Tools</a></li>
<li class="toctree-l1"><a class="reference internal" href="why-foundationdb.html">Why FoundationDB</a><ul>
<li class="toctree-l2"><a class="reference internal" href="transaction-manifesto.html">Transaction Manifesto</a></li>
<li class="toctree-l2"><a class="reference internal" href="cap-theorem.html">CAP Theorem</a></li>
<li class="toctree-l2"><a class="reference internal" href="consistency.html">Consistency</a></li>
<li class="toctree-l2"><a class="reference internal" href="scalability.html">Scalability</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="technical-overview.html">Technical Overview</a><ul>
<li class="toctree-l2"><a class="reference internal" href="architecture.html">Architecture</a></li>
<li class="toctree-l2"><a class="reference internal" href="performance.html">Performance</a></li>
<li class="toctree-l2"><a class="reference internal" href="benchmarking.html">Benchmarking</a></li>
<li class="toctree-l2"><a class="reference internal" href="engineering.html">Engineering</a></li>
<li class="toctree-l2"><a class="reference internal" href="features.html">Features</a></li>
<li class="toctree-l2"><a class="reference internal" href="layer-concept.html">Layer Concept</a></li>
<li class="toctree-l2"><a class="reference internal" href="anti-features.html">Anti-Features</a></li>
<li class="toctree-l2"><a class="reference internal" href="experimental-features.html">Experimental-Features</a></li>
<li class="toctree-l2"><a class="reference internal" href="transaction-processing.html">Transaction Processing</a></li>
<li class="toctree-l2"><a class="reference internal" href="fault-tolerance.html">Fault Tolerance</a></li>
<li class="toctree-l2"><a class="reference internal" href="flow.html">Flow</a></li>
<li class="toctree-l2"><a class="reference internal" href="testing.html">Simulation and Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="kv-architecture.html">FoundationDB Architecture</a></li>
<li class="toctree-l2"><a class="reference internal" href="read-write-path.html">FDB Read and Write Path</a></li>
<li class="toctree-l2"><a class="reference internal" href="ha-write-path.html">FDB HA Write Path: How a mutation travels in FDB HA</a></li>
<li class="toctree-l2"><a class="reference internal" href="consistency-check-urgent.html">Consistency Checker Urgent</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="client-design.html">Client Design</a><ul>
<li class="toctree-l2"><a class="reference internal" href="getting-started-mac.html">Getting Started on macOS</a></li>
<li class="toctree-l2"><a class="reference internal" href="getting-started-linux.html">Getting Started on Linux</a></li>
<li class="toctree-l2"><a class="reference internal" href="downloads.html">Downloads</a></li>
<li class="toctree-l2"><a class="reference internal" href="developer-guide.html">Developer Guide</a></li>
<li class="toctree-l2"><a class="reference internal" href="data-modeling.html">Data Modeling</a></li>
<li class="toctree-l2"><a class="reference internal" href="client-testing.html">Client Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="client-testing.html#testing-error-handling-with-buggify">Testing Error Handling with Buggify</a></li>
<li class="toctree-l2"><a class="reference internal" href="client-testing.html#simulation-and-cluster-workloads">Simulation and Cluster Workloads</a></li>
<li class="toctree-l2"><a class="reference internal" href="client-testing.html#api-tester">API Tester</a></li>
<li class="toctree-l2"><a class="reference internal" href="api-general.html">Using FoundationDB Clients</a></li>
<li class="toctree-l2"><a class="reference internal" href="transaction-tagging.html">Transaction Tagging</a></li>
<li class="toctree-l2"><a class="reference internal" href="known-limitations.html">Known Limitations</a></li>
<li class="toctree-l2"><a class="reference internal" href="transaction-profiler-analyzer.html">Transaction profiling and analyzing</a></li>
<li class="toctree-l2"><a class="reference internal" href="api-version-upgrade-guide.html">API Version Upgrade Guide</a></li>
<li class="toctree-l2"><a class="reference internal" href="tenants.html">Tenants</a></li>
<li class="toctree-l2"><a class="reference internal" href="automatic-idempotency.html">Automatic Idempotency</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="design-recipes.html">Design Recipes</a><ul>
<li class="toctree-l2"><a class="reference internal" href="blob.html">Blob</a></li>
<li class="toctree-l2"><a class="reference internal" href="blob-java.html">Blob</a></li>
<li class="toctree-l2"><a class="reference internal" href="hierarchical-documents.html">Hierarchical Documents</a></li>
<li class="toctree-l2"><a class="reference internal" href="hierarchical-documents-java.html">Hierarchical Documents</a></li>
<li class="toctree-l2"><a class="reference internal" href="multimaps.html">Multimaps</a></li>
<li class="toctree-l2"><a class="reference internal" href="multimaps-java.html">Multimaps</a></li>
<li class="toctree-l2"><a class="reference internal" href="priority-queues.html">Priority Queues</a></li>
<li class="toctree-l2"><a class="reference internal" href="priority-queues-java.html">Priority Queues</a></li>
<li class="toctree-l2"><a class="reference internal" href="queues.html">Queues</a></li>
<li class="toctree-l2"><a class="reference internal" href="queues-java.html">Queues</a></li>
<li class="toctree-l2"><a class="reference internal" href="segmented-range-reads.html">Segmented Range Reads</a></li>
<li class="toctree-l2"><a class="reference internal" href="segmented-range-reads-java.html">Segmented Range Reads</a></li>
<li class="toctree-l2"><a class="reference internal" href="simple-indexes.html">Simple Indexes</a></li>
<li class="toctree-l2"><a class="reference internal" href="simple-indexes-java.html">Simple Indexes</a></li>
<li class="toctree-l2"><a class="reference internal" href="spatial-indexing.html">Spatial Indexing</a></li>
<li class="toctree-l2"><a class="reference internal" href="spatial-indexing-java.html">Spatial Indexing</a></li>
<li class="toctree-l2"><a class="reference internal" href="subspace-indirection.html">Subspace Indirection</a></li>
<li class="toctree-l2"><a class="reference internal" href="subspace-indirection-java.html">Subspace Indirection</a></li>
<li class="toctree-l2"><a class="reference internal" href="tables.html">Tables</a></li>
<li class="toctree-l2"><a class="reference internal" href="tables-java.html">Tables</a></li>
<li class="toctree-l2"><a class="reference internal" href="vector.html">Vector</a></li>
<li class="toctree-l2"><a class="reference internal" href="vector-java.html">Vector</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="api-reference.html">API Reference</a><ul>
<li class="toctree-l2"><a class="reference internal" href="api-python.html">Python API</a></li>
<li class="toctree-l2"><a class="reference internal" href="api-ruby.html">Ruby API</a></li>
<li class="toctree-l2"><a class="reference external" href="relative://javadoc/index.html">Java API</a></li>
<li class="toctree-l2"><a class="reference external" href="https://godoc.org/github.com/apple/foundationdb/bindings/go/src/fdb">Go API</a></li>
<li class="toctree-l2"><a class="reference internal" href="api-c.html">C API</a></li>
<li class="toctree-l2"><a class="reference internal" href="api-error-codes.html">Error Codes</a></li>
<li class="toctree-l2"><a class="reference internal" href="special-keys.html">Special Keys</a></li>
<li class="toctree-l2"><a class="reference internal" href="global-configuration.html">Global Configuration</a></li>
</ul>
</li>
<li class="toctree-l1 current"><a class="reference internal" href="tutorials.html">Tutorials</a><ul class="current">
<li class="toctree-l2 current"><a class="current reference internal" href="#">Class Scheduling</a></li>
<li class="toctree-l2"><a class="reference internal" href="largeval.html">Managing Large Values and Blobs</a></li>
<li class="toctree-l2"><a class="reference internal" href="time-series.html">Time-Series Data</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="administration.html">Administration</a><ul>
<li class="toctree-l2"><a class="reference internal" href="configuration.html">Configuration</a></li>
<li class="toctree-l2"><a class="reference internal" href="moving-a-cluster.html">Moving a Cluster to New Machines</a></li>
<li class="toctree-l2"><a class="reference internal" href="tls.html">Transport Layer Security</a></li>
<li class="toctree-l2"><a class="reference internal" href="authorization.html">Authorization</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="monitored-metrics.html"><strong>Monitored Metrics</strong></a></li>
<li class="toctree-l1"><a class="reference internal" href="redwood.html">Redwood Storage Engine</a></li>
<li class="toctree-l1"><a class="reference internal" href="visibility.html">Visibility Documents</a><ul>
<li class="toctree-l2"><a class="reference internal" href="request-tracing.html">Request Tracing</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="earlier-release-notes.html">Earlier Release Notes</a><ul>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-014.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-016.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-021.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-022.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-023.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-100.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-200.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-300.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-400.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-410.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-420.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-430.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-440.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-450.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-460.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-500.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-510.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-520.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-600.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-610.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-620.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-630.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-700.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-710.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-720.html">Release Notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="release-notes/release-notes-730.html">Release Notes</a></li>
</ul>
</li>
</ul>
</ul>
</li>
<li class="dropdown">
<a role="button"
id="dLabelLocalToc"
data-toggle="dropdown"
data-target="#"
href="#">Page <b class="caret"></b></a>
<ul class="dropdown-menu localtoc"
role="menu"
aria-labelledby="dLabelLocalToc"><ul>
<li><a class="reference internal" href="#">Class Scheduling</a><ul>
<li><a class="reference internal" href="#first-steps">First steps</a></li>
<li><a class="reference internal" href="#class-scheduling-application">Class scheduling application</a><ul>
<li><a class="reference internal" href="#requirements">Requirements</a></li>
<li><a class="reference internal" href="#data-model">Data model</a></li>
<li><a class="reference internal" href="#directories-and-subspaces">Directories and Subspaces</a></li>
<li><a class="reference internal" href="#transactions">Transactions</a></li>
<li><a class="reference internal" href="#making-some-sample-classes">Making some sample classes</a></li>
<li><a class="reference internal" href="#initializing-the-database">Initializing the database</a></li>
<li><a class="reference internal" href="#listing-available-classes">Listing available classes</a></li>
<li><a class="reference internal" href="#signing-up-for-a-class">Signing up for a class</a></li>
<li><a class="reference internal" href="#dropping-a-class">Dropping a class</a></li>
<li><a class="reference internal" href="#done">Done?</a></li>
<li><a class="reference internal" href="#seats-are-limited">Seats are limited!</a></li>
<li><a class="reference internal" href="#concurrency-and-consistency">Concurrency and consistency</a></li>
<li><a class="reference internal" href="#idempotence">Idempotence</a></li>
<li><a class="reference internal" href="#more-features">More features?!</a></li>
<li><a class="reference internal" href="#composing-transactions">Composing transactions</a></li>
<li><a class="reference internal" href="#are-we-done">Are we done?</a></li>
<li><a class="reference internal" href="#deploying-and-scaling">Deploying and scaling</a></li>
</ul>
</li>
<li><a class="reference internal" href="#next-steps">Next steps</a></li>
<li><a class="reference internal" href="#appendix-schedulingtutorial-py">Appendix: SchedulingTutorial.py</a></li>
</ul>
</li>
</ul>
</ul>
</li>
<li>
<a href="tutorials.html" title="Previous Chapter: Tutorials"><span class="glyphicon glyphicon-chevron-left visible-sm"></span><span class="hidden-sm hidden-tablet">&laquo; Tutorials</span>
</a>
</li>
<li>
<a href="class-scheduling-ruby.html" title="Next Chapter: Class Scheduling in Ruby"><span class="glyphicon glyphicon-chevron-right visible-sm"></span><span class="hidden-sm hidden-tablet">Class Schedul... &raquo;</span>
</a>
</li>
</ul>
<form class="navbar-form navbar-right" action="search.html" method="get">
<div class="form-group">
<input type="text" name="q" class="form-control" placeholder="Search" />
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3">
<div id="sidebar" class="bs-sidenav" role="complementary"><ul>
<li><a class="reference internal" href="#">Class Scheduling</a><ul>
<li><a class="reference internal" href="#first-steps">First steps</a></li>
<li><a class="reference internal" href="#class-scheduling-application">Class scheduling application</a><ul>
<li><a class="reference internal" href="#requirements">Requirements</a></li>
<li><a class="reference internal" href="#data-model">Data model</a></li>
<li><a class="reference internal" href="#directories-and-subspaces">Directories and Subspaces</a></li>
<li><a class="reference internal" href="#transactions">Transactions</a></li>
<li><a class="reference internal" href="#making-some-sample-classes">Making some sample classes</a></li>
<li><a class="reference internal" href="#initializing-the-database">Initializing the database</a></li>
<li><a class="reference internal" href="#listing-available-classes">Listing available classes</a></li>
<li><a class="reference internal" href="#signing-up-for-a-class">Signing up for a class</a></li>
<li><a class="reference internal" href="#dropping-a-class">Dropping a class</a></li>
<li><a class="reference internal" href="#done">Done?</a></li>
<li><a class="reference internal" href="#seats-are-limited">Seats are limited!</a></li>
<li><a class="reference internal" href="#concurrency-and-consistency">Concurrency and consistency</a></li>
<li><a class="reference internal" href="#idempotence">Idempotence</a></li>
<li><a class="reference internal" href="#more-features">More features?!</a></li>
<li><a class="reference internal" href="#composing-transactions">Composing transactions</a></li>
<li><a class="reference internal" href="#are-we-done">Are we done?</a></li>
<li><a class="reference internal" href="#deploying-and-scaling">Deploying and scaling</a></li>
</ul>
</li>
<li><a class="reference internal" href="#next-steps">Next steps</a></li>
<li><a class="reference internal" href="#appendix-schedulingtutorial-py">Appendix: SchedulingTutorial.py</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="body col-md-9 content" role="main">
<section id="class-scheduling">
<h1>Class Scheduling</h1>
<p>This tutorial provides a walkthrough of designing and building a simple application in Python using FoundationDB. In this tutorial, we use a few simple data modeling techniques. For a more in-depth discussion of data modeling in FoundationDB, see <a class="reference internal" href="data-modeling.html"><span class="doc">Data Modeling</span></a>.</p>
<p>The concepts in this tutorial are applicable to all the <a class="reference internal" href="api-reference.html"><span class="doc">languages</span></a> supported by FoundationDB. If you prefer, you can see a version of this tutorial in:</p>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="class-scheduling-ruby.html">Ruby</a></li>
<li class="toctree-l1"><a class="reference internal" href="class-scheduling-java.html">Java</a></li>
<li class="toctree-l1"><a class="reference internal" href="class-scheduling-go.html">Go</a></li>
</ul>
</div>
<section id="first-steps">
<span id="tutorial-first-steps"></span><h2>First steps</h2>
<p>Lets begin with “Hello world.”</p>
<p>If you have not yet installed FoundationDB, see <a class="reference internal" href="getting-started-mac.html"><span class="doc">Getting Started on macOS</span></a> or <a class="reference internal" href="getting-started-linux.html"><span class="doc">Getting Started on Linux</span></a>.</p>
<p>Open a Python interactive interpreter and import the FoundationDB API module:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ python
&gt;&gt;&gt; import fdb
</pre></div>
</div>
<p>Before using the API, we need to specify the API version. This allows programs to maintain compatibility even if the API is modified in future versions:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">fdb</span><span class="o">.</span><span class="n">api_version</span><span class="p">(</span><span class="mi">730</span><span class="p">)</span>
</pre></div>
</div>
<p>Next, we open a FoundationDB database. The API will connect to the FoundationDB cluster indicated by the <a class="reference internal" href="administration.html#default-cluster-file"><span class="std std-ref">default cluster file</span></a>.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
</pre></div>
</div>
<p>We are ready to use the database. In Python, using the <code class="docutils literal notranslate"><span class="pre">[]</span></code> operator on the db object is a convenient syntax for performing a read or write on the database. First, lets simply write a key-value pair:</p>
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">db</span><span class="p">[</span><span class="sa">b</span><span class="s1">&#39;hello&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;world&#39;</span>
</pre></div>
</div>
<p>When this command returns without exception, the modification is durably stored in FoundationDB! Under the covers, this function creates a transaction with a single modification. Well see later how to do multiple operations in a single transaction. For now, lets read back the data:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span> <span class="s1">&#39;hello&#39;</span><span class="p">,</span> <span class="n">db</span><span class="p">[</span><span class="sa">b</span><span class="s1">&#39;hello&#39;</span><span class="p">]</span>
<span class="go">hello world</span>
</pre></div>
</div>
<p>If this is all working, it looks like we are ready to start building a real application. For reference, heres the full code for “hello world”:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">fdb</span>
<span class="n">fdb</span><span class="o">.</span><span class="n">api_version</span><span class="p">(</span><span class="mi">730</span><span class="p">)</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
<span class="n">db</span><span class="p">[</span><span class="sa">b</span><span class="s1">&#39;hello&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;world&#39;</span>
<span class="nb">print</span> <span class="s1">&#39;hello&#39;</span><span class="p">,</span> <span class="n">db</span><span class="p">[</span><span class="sa">b</span><span class="s1">&#39;hello&#39;</span><span class="p">]</span>
</pre></div>
</div>
</section>
<section id="class-scheduling-application">
<h2>Class scheduling application</h2>
<p>Lets say weve been asked to build a class scheduling system for students and administrators. Well walk through the design and implementation of this application. Instead of typing everything in as you follow along, look at the <a class="reference internal" href="#tutorial-appendix"><span class="std std-ref">Appendix: SchedulingTutorial.py</span></a> for a finished version of the program. You may want to refer to this code as we walk through the tutorial.</p>
<section id="requirements">
<h3>Requirements</h3>
<p>Well need to let users list available classes and track which students have signed up for which classes. Heres a first cut at the functions well need to implement:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">available_classes</span><span class="p">()</span> <span class="c1"># returns list of classes</span>
<span class="n">signup</span><span class="p">(</span><span class="n">studentID</span><span class="p">,</span> <span class="n">class</span><span class="p">)</span> <span class="c1"># signs up a student for a class</span>
<span class="n">drop</span><span class="p">(</span><span class="n">studentID</span><span class="p">,</span> <span class="n">class</span><span class="p">)</span> <span class="c1"># drops a student from a class</span>
</pre></div>
</div>
</section>
<section id="data-model">
<span id="tutorial-data-model"></span><h3>Data model</h3>
<p>First, we need to design a <a class="reference internal" href="data-modeling.html"><span class="doc">data model</span></a>. A data model is just a method for storing our application data using keys and values in FoundationDB. We seem to have two main types of data: (1) a list of classes and (2) a record of which students will attend which classes. Lets keep attending data like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># (&#39;attends&#39;, student, class) = &#39;&#39;</span>
</pre></div>
</div>
<p>Well just store the key with a blank value to indicate that a student is signed up for a particular class. For this application, were going to think about a key-value pairs key as a <a class="reference internal" href="data-modeling.html#data-modeling-tuples"><span class="std std-ref">tuple</span></a>. Encoding a tuple of data elements into a key is a very common pattern for an ordered key-value store.</p>
<p>Well keep data about classes like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># (&#39;class&#39;, class_name) = seats_available</span>
</pre></div>
</div>
<p>Similarly, each such key will represent an available class. Well use <code class="docutils literal notranslate"><span class="pre">seats_available</span></code> to record the number of seats available.</p>
</section>
<section id="directories-and-subspaces">
<h3>Directories and Subspaces</h3>
<p>FoundationDB includes a few tools that make it easy to model data using this approach. Lets begin by
opening a <a class="reference internal" href="developer-guide.html#developer-guide-directories"><span class="std std-ref">directory</span></a> in the database:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">fdb</span>
<span class="n">fdb</span><span class="o">.</span><span class="n">api_version</span><span class="p">(</span><span class="mi">730</span><span class="p">)</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
<span class="n">scheduling</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">directory</span><span class="o">.</span><span class="n">create_or_open</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="p">(</span><span class="s1">&#39;scheduling&#39;</span><span class="p">,))</span>
</pre></div>
</div>
<p>The <code class="xref py py-meth docutils literal notranslate"><span class="pre">create_or_open()</span></code> method returns a <a class="reference internal" href="developer-guide.html#developer-guide-sub-keyspaces"><span class="std std-ref">subspace</span></a> where well store our application data. Each subspace has a fixed prefix it uses when defining keys. The prefix corresponds to the first element of a tuple. We decided that we wanted <code class="docutils literal notranslate"><span class="pre">'attends'</span></code> and <code class="docutils literal notranslate"><span class="pre">'class'</span></code> as our prefixes, so well create new subspaces for them within the <code class="docutils literal notranslate"><span class="pre">scheduling</span></code> subspace.:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">course</span> <span class="o">=</span> <span class="n">scheduling</span><span class="p">[</span><span class="s1">&#39;class&#39;</span><span class="p">]</span>
<span class="n">attends</span> <span class="o">=</span> <span class="n">scheduling</span><span class="p">[</span><span class="s1">&#39;attends&#39;</span><span class="p">]</span>
</pre></div>
</div>
<p>Subspaces have a <code class="xref py py-meth docutils literal notranslate"><span class="pre">pack()</span></code> method for defining keys. To store the records for our data model, we can use <code class="docutils literal notranslate"><span class="pre">attends.pack((s,</span> <span class="pre">c))</span></code> and <code class="docutils literal notranslate"><span class="pre">course.pack((c,))</span></code>.</p>
</section>
<section id="transactions">
<h3>Transactions</h3>
<p>Were going to rely on the powerful guarantees of transactions to help keep all of our modifications straight, so lets look at a nice way that the FoundationDB Python API lets you write a transactional function. By using a decorator, an entire function is wrapped in a transaction. Lets write the very simple <code class="docutils literal notranslate"><span class="pre">add_class</span></code> function we will use to populate the databases class list:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">add_class</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="mi">100</span><span class="p">,))</span>
</pre></div>
</div>
<p><a class="reference internal" href="api-python.html#fdb.transactional" title="fdb.transactional"><code class="xref py py-func docutils literal notranslate"><span class="pre">&#64;fdb.transactional</span></code></a> is a Python decorator that makes a normal function a transactional function. All functions decorated this way <em>need to have a parameter named</em> <code class="docutils literal notranslate"><span class="pre">tr</span></code>. This parameter is passed the transaction that the function should use to do reads and writes.</p>
<p>When <em>calling</em> a transactionally decorated function, however, you can pass a database instead of a transaction for the <code class="docutils literal notranslate"><span class="pre">tr</span></code> parameter. The decorator <em>automatically creates a transaction and implements a retry loop</em> to ensure that the transaction eventually commits.</p>
<p>For a FoundationDB database <code class="docutils literal notranslate"><span class="pre">db</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">add_class</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="s1">&#39;class1&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>is equivalent to something like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">tr</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">create_transaction</span><span class="p">()</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">add_class</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="s1">&#39;class1&#39;</span><span class="p">)</span>
<span class="n">tr</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
<span class="k">break</span>
<span class="k">except</span> <span class="n">fdb</span><span class="o">.</span><span class="n">FDBError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">tr</span><span class="o">.</span><span class="n">on_error</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
</pre></div>
</div>
<p>If instead you pass a <code class="xref py py-class docutils literal notranslate"><span class="pre">Transaction</span></code> for the <code class="docutils literal notranslate"><span class="pre">tr</span></code> parameter, the transaction will be used directly, and it is assumed that the caller implements appropriate retry logic for errors. This permits transactionally decorated functions to be composed into larger transactions.</p>
<p>Note that by default, the operation will be retried an infinite number of times and the transaction will never time out. It is therefore recommended that the client choose a default transaction retry limit or timeout value that is suitable for their application. This can be set either at the transaction level using the <code class="docutils literal notranslate"><span class="pre">set_retry_limit</span></code> or <code class="docutils literal notranslate"><span class="pre">set_timeout</span></code> transaction options or at the database level with the <code class="docutils literal notranslate"><span class="pre">set_transaction_retry_limit</span></code> or <code class="docutils literal notranslate"><span class="pre">set_transaction_timeout</span></code> database options. For example, one can set a one minute timeout on each transaction and a default retry limit of 100 by calling:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">db</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">set_transaction_timeout</span><span class="p">(</span><span class="mi">60000</span><span class="p">)</span> <span class="c1"># 60,000 ms = 1 minute</span>
<span class="n">db</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">set_transaction_retry_limit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="making-some-sample-classes">
<h3>Making some sample classes</h3>
<p>Lets make some sample classes and put them in the <code class="docutils literal notranslate"><span class="pre">class_names</span></code> variable. The Python <code class="docutils literal notranslate"><span class="pre">itertools</span></code> module is used to make individual classes from combinations of class types, levels, and times:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">itertools</span>
<span class="c1"># Generate 1,620 classes like &#39;9:00 chem for dummies&#39;</span>
<span class="n">levels</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;intro&#39;</span><span class="p">,</span> <span class="s1">&#39;for dummies&#39;</span><span class="p">,</span> <span class="s1">&#39;remedial&#39;</span><span class="p">,</span> <span class="s1">&#39;101&#39;</span><span class="p">,</span>
<span class="s1">&#39;201&#39;</span><span class="p">,</span> <span class="s1">&#39;301&#39;</span><span class="p">,</span> <span class="s1">&#39;mastery&#39;</span><span class="p">,</span> <span class="s1">&#39;lab&#39;</span><span class="p">,</span> <span class="s1">&#39;seminar&#39;</span><span class="p">]</span>
<span class="n">types</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;chem&#39;</span><span class="p">,</span> <span class="s1">&#39;bio&#39;</span><span class="p">,</span> <span class="s1">&#39;cs&#39;</span><span class="p">,</span> <span class="s1">&#39;geometry&#39;</span><span class="p">,</span> <span class="s1">&#39;calc&#39;</span><span class="p">,</span>
<span class="s1">&#39;alg&#39;</span><span class="p">,</span> <span class="s1">&#39;film&#39;</span><span class="p">,</span> <span class="s1">&#39;music&#39;</span><span class="p">,</span> <span class="s1">&#39;art&#39;</span><span class="p">,</span> <span class="s1">&#39;dance&#39;</span><span class="p">]</span>
<span class="n">times</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;:00&#39;</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">20</span><span class="p">)]</span>
<span class="n">class_combos</span> <span class="o">=</span> <span class="n">itertools</span><span class="o">.</span><span class="n">product</span><span class="p">(</span><span class="n">times</span><span class="p">,</span> <span class="n">types</span><span class="p">,</span> <span class="n">levels</span><span class="p">)</span>
<span class="n">class_names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">tup</span><span class="p">)</span> <span class="k">for</span> <span class="n">tup</span> <span class="ow">in</span> <span class="n">class_combos</span><span class="p">]</span>
</pre></div>
</div>
</section>
<section id="initializing-the-database">
<h3>Initializing the database</h3>
<p>We initialize the database with our class list:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">tr</span><span class="p">):</span>
<span class="k">del</span> <span class="n">tr</span><span class="p">[</span><span class="n">scheduling</span><span class="o">.</span><span class="n">range</span><span class="p">(())]</span> <span class="c1"># Clear the directory</span>
<span class="k">for</span> <span class="n">class_name</span> <span class="ow">in</span> <span class="n">class_names</span><span class="p">:</span>
<span class="n">add_class</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">class_name</span><span class="p">)</span>
</pre></div>
</div>
<p>After <code class="xref py py-func docutils literal notranslate"><span class="pre">init()</span></code> is run, the database will contain all of the sample classes we created above.</p>
</section>
<section id="listing-available-classes">
<h3>Listing available classes</h3>
<p>Before students can do anything else, they need to be able to retrieve a list of available classes from the database. Because FoundationDB sorts its data by key and therefore has efficient range-read capability, we can retrieve all of the classes in a single database call. We find this range of keys with <code class="xref py py-meth docutils literal notranslate"><span class="pre">course.range()</span></code>.:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">available_classes</span><span class="p">(</span><span class="n">tr</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">k</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">range</span><span class="p">(())]]</span>
</pre></div>
</div>
<p>In general, the <code class="xref py py-meth docutils literal notranslate"><span class="pre">Subspace.range()</span></code> method returns a Python <code class="docutils literal notranslate"><span class="pre">slice</span></code> representing all the key-value pairs starting with the specified tuple. In this case, we want all classes, so we call <code class="xref py py-meth docutils literal notranslate"><span class="pre">course.range()</span></code> with the empty tuple <code class="docutils literal notranslate"><span class="pre">()</span></code>. FoundationDBs <code class="docutils literal notranslate"><span class="pre">tr[slice]</span></code> function returns an iterable list of key-values in the range specified by the slice. We unpack the key <code class="docutils literal notranslate"><span class="pre">k</span></code> and value <code class="docutils literal notranslate"><span class="pre">v</span></code> in a comprehension. To extract the class name itself, we unpack the key into a tuple using the <code class="xref py py-meth docutils literal notranslate"><span class="pre">Subspace.unpack()</span></code> method and take the first field. (The first and second parts of the tuple, the <code class="docutils literal notranslate"><span class="pre">scheduling</span></code> and <code class="docutils literal notranslate"><span class="pre">course</span></code> subspace prefixes, are removed by the <code class="docutils literal notranslate"><span class="pre">unpack</span></code> hence the reason we take the first field of the tuple.)</p>
</section>
<section id="signing-up-for-a-class">
<h3>Signing up for a class</h3>
<p>We finally get to the crucial function. A student has decided on a class (by name) and wants to sign up. The <code class="docutils literal notranslate"><span class="pre">signup</span></code> function will take a student (<code class="docutils literal notranslate"><span class="pre">s</span></code>) and a class (<code class="docutils literal notranslate"><span class="pre">c</span></code>):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">signup</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;&#39;</span>
</pre></div>
</div>
<p>We simply insert the appropriate record (with a blank value).</p>
</section>
<section id="dropping-a-class">
<h3>Dropping a class</h3>
<p>Dropping a class is similar to signing up:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">drop</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="k">del</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span>
</pre></div>
</div>
<p>Of course, to actually drop the student from the class, we need to be able to delete a record from the database. We do this with the <code class="docutils literal notranslate"><span class="pre">del</span> <span class="pre">tr[key]</span></code> syntax.</p>
</section>
<section id="done">
<h3>Done?</h3>
<p>We report back to the project leader that our application is done—students can sign up for, drop, and list classes. Unfortunately, we learn that a new problem has been discovered: popular classes are being over-subscribed. Our application now needs to enforce the class size constraint as students add and drop classes.</p>
</section>
<section id="seats-are-limited">
<h3>Seats are limited!</h3>
<p>Lets go back to the data model. Remember that we stored the number of seats in the class in the value of the key-value entry in the class list. Lets refine that a bit to track the <em>remaining</em> number of seats in the class. The initialization can work the same way. (In our example, all classes initially have 100 seats), but the <code class="docutils literal notranslate"><span class="pre">available_classes</span></code>, <code class="docutils literal notranslate"><span class="pre">signup</span></code>, and <code class="docutils literal notranslate"><span class="pre">drop</span></code> functions are going to have to change. Lets start with <code class="docutils literal notranslate"><span class="pre">available_classes</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">available_classes</span><span class="p">(</span><span class="n">tr</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">k</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">range</span><span class="p">(())]</span>
<span class="hll"> <span class="k">if</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">v</span><span class="p">)[</span><span class="mi">0</span><span class="p">]]</span>
</span></pre></div>
</div>
<p>This is easy we simply add a condition to check that the value is non-zero. Lets check out <code class="docutils literal notranslate"><span class="pre">signup</span></code> next:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">signup</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="hll"> <span class="k">if</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span><span class="o">.</span><span class="n">present</span><span class="p">():</span> <span class="k">return</span> <span class="c1"># already signed up</span>
</span><span class="hll">
</span><span class="hll"> <span class="n">seats_left</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))])[</span><span class="mi">0</span><span class="p">]</span>
</span><span class="hll"> <span class="k">if</span> <span class="ow">not</span> <span class="n">seats_left</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">&#39;No remaining seats&#39;</span><span class="p">)</span>
</span><span class="hll">
</span><span class="hll"> <span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">seats_left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,))</span>
</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;&#39;</span>
</pre></div>
</div>
<p>We now have to check that we arent already signed up, since we dont want a double sign up to decrease the number of seats twice. Then we look up how many seats are left to make sure there is a seat remaining so we dont push the counter into the negative. If there is a seat remaining, we decrement the counter.</p>
<p>Similarly, the <code class="docutils literal notranslate"><span class="pre">drop</span></code> function is modified as follows:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">drop</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="hll"> <span class="k">if</span> <span class="ow">not</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span><span class="o">.</span><span class="n">present</span><span class="p">():</span> <span class="k">return</span> <span class="c1"># not taking this class</span>
</span><span class="hll"> <span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))])[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,))</span>
</span> <span class="k">del</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span>
</pre></div>
</div>
<p>Once again we check to see if the student is signed up and if not, we can just return as we dont want to incorrectly increase the number of seats. We then adjust the number of seats by one by taking the current value, incrementing it by one, and then storing back.</p>
</section>
<section id="concurrency-and-consistency">
<h3>Concurrency and consistency</h3>
<p>The <code class="docutils literal notranslate"><span class="pre">signup</span></code> function is starting to get a bit complex; it now reads and writes a few different key-value pairs in the database. One of the tricky issues in this situation is what happens as multiple clients/students read and modify the database at the same time. Couldnt two students both see one remaining seat and sign up at the same time?</p>
<p>These are tricky issues without simple answers—unless you have transactions! Because these functions are defined as FoundationDB transactions, we can have a simple answer: Each transactional function behaves as if it is the only one modifying the database. There is no way for a transaction to see another transaction change the database, and each transaction ensures that either all of its modifications occur or none of them do.</p>
<p>Looking deeper, it is, of course, possible for two transactions to conflict. For example, if two people both see a class with one seat and sign up at the same time, FoundationDB must allow only one to succeed. This causes one of the transactions to fail to commit (which can also be caused by network outages, crashes, etc.). To ensure correct operation, applications need to handle this situation, usually via retrying the transaction. In this case, the conflicting transaction will be retried automatically by the <code class="docutils literal notranslate"><span class="pre">&#64;fdb.transactional</span></code> decorator and will eventually lead to the correct result, a No remaining seats exception.</p>
</section>
<section id="idempotence">
<h3>Idempotence</h3>
<p>Occasionally, a transaction might be retried even after it succeeds (for example, if the client loses contact with the cluster at just the wrong moment). This can cause problems if transactions are not written to be idempotent, i.e. to have the same effect if committed twice as if committed once. There are generic design patterns for <a class="reference internal" href="developer-guide.html#developer-guide-unknown-results"><span class="std std-ref">making any transaction idempotent</span></a>, but many transactions are naturally idempotent. For example, all of the transactions in this tutorial are idempotent.</p>
</section>
<section id="more-features">
<h3>More features?!</h3>
<p>Of course, as soon as our new version of the system goes live, we hear of a trick that certain students are using. They are signing up for all classes immediately, and only later dropping those that they dont want to take. This has led to an unusable system, and we have been asked to fix it. We decide to limit students to five classes:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">signup</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="k">if</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span><span class="o">.</span><span class="n">present</span><span class="p">():</span> <span class="k">return</span> <span class="c1"># already signed up</span>
<span class="n">seats_left</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))])[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">seats_left</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">&#39;No remaining seats&#39;</span><span class="p">)</span>
<span class="hll"> <span class="n">classes</span> <span class="o">=</span> <span class="n">tr</span><span class="p">[</span><span class="n">attends</span><span class="o">.</span><span class="n">range</span><span class="p">((</span><span class="n">s</span><span class="p">,))]</span>
</span><span class="hll"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">classes</span><span class="p">))</span> <span class="o">==</span> <span class="mi">5</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">&#39;Too many classes&#39;</span><span class="p">)</span>
</span>
<span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">seats_left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,))</span>
<span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;&#39;</span>
</pre></div>
</div>
<p>Fortunately, we decided on a data model that keeps all of the attending records for a single student together. With this approach, we can use a single range read in the <code class="docutils literal notranslate"><span class="pre">attends</span></code> subspace to retrieve all the classes that a student is signed up for. We simply throw an exception if the number of classes has reached the limit of five.</p>
</section>
<section id="composing-transactions">
<h3>Composing transactions</h3>
<p>Oh, just one last feature, were told. We have students that are trying to switch from one popular class to another. By the time they drop one class to free up a slot for themselves, the open slot in the other class is gone. By the time they see this and try to re-add their old class, that slot is gone too! So, can we make it so that a student can switch from one class to another without this worry?</p>
<p>Fortunately, we have FoundationDB, and this sounds an awful lot like the transactional property of atomicity—the all-or-nothing behavior that we already rely on. All we need to do is to <em>compose</em> the <code class="docutils literal notranslate"><span class="pre">drop</span></code> and <code class="docutils literal notranslate"><span class="pre">signup</span></code> functions into a new <code class="docutils literal notranslate"><span class="pre">switch</span></code> function. This makes the <code class="docutils literal notranslate"><span class="pre">switch</span></code> function exceptionally easy:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">switch</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">old_c</span><span class="p">,</span> <span class="n">new_c</span><span class="p">):</span>
<span class="n">drop</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">old_c</span><span class="p">)</span>
<span class="n">signup</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">new_c</span><span class="p">)</span>
</pre></div>
</div>
<p>The simplicity of this implementation belies the sophistication of what FoundationDB is taking care of for us.</p>
<p>By dropping the old class and signing up for the new one inside a single transaction, we ensure that either both steps happen, or that neither happens. The first notable thing about the <code class="docutils literal notranslate"><span class="pre">switch</span></code> function is that it is transactionally decorated, but it also calls the transactionally decorated functions <code class="docutils literal notranslate"><span class="pre">signup</span></code> and <code class="docutils literal notranslate"><span class="pre">drop</span></code>. Because these decorated functions can accept either a database or an existing transaction as the <code class="docutils literal notranslate"><span class="pre">tr</span></code> argument, the switch function can be called with a database by a simple client, and a new transaction will be automatically created. However, once this transaction is created and passed in as <code class="docutils literal notranslate"><span class="pre">tr</span></code>, the calls to <code class="docutils literal notranslate"><span class="pre">drop</span></code> and <code class="docutils literal notranslate"><span class="pre">signup</span></code> both share the same <code class="docutils literal notranslate"><span class="pre">tr</span></code>. This ensures that they see each others modifications to the database, and all of the changes that both of them make in sequence are made transactionally when the switch function returns. This compositional capability is very powerful.</p>
<p>Also note that, if an exception is raised, for example, in <code class="docutils literal notranslate"><span class="pre">signup</span></code>, the exception is not caught by <code class="docutils literal notranslate"><span class="pre">switch</span></code> and so will be thrown to the calling function. In this case, the transaction object (owned by the decorator) is destroyed, automatically rolling back all database modifications, leaving the database completely unchanged by the half-executed function.</p>
</section>
<section id="are-we-done">
<h3>Are we done?</h3>
<p>Yep, were done and ready to deploy. If you want to see this entire application in one place plus some multithreaded testing code to simulate concurrency, look at the <a class="reference internal" href="#tutorial-appendix"><span class="std std-ref">Appendix: SchedulingTutorial.py</span></a>, below.</p>
</section>
<section id="deploying-and-scaling">
<h3>Deploying and scaling</h3>
<p>Since we store all state for this application in FoundationDB, deploying and scaling this solution up is impressively painless. Just run a web server, the UI, this back end, and point the whole thing at FoundationDB. We can run as many computers with this setup as we want, and they can all hit the database at the same time because of the transactional integrity of FoundationDB. Also, since all of the state in the system is stored in the database, any of these computers can fail without any lasting consequences.</p>
</section>
</section>
<section id="next-steps">
<h2>Next steps</h2>
<ul class="simple">
<li><p>See <a class="reference internal" href="data-modeling.html"><span class="doc">Data Modeling</span></a> for guidance on using tuple and subspaces to enable effective storage and retrieval of data.</p></li>
<li><p>See <a class="reference internal" href="developer-guide.html"><span class="doc">Developer Guide</span></a> for general guidance on development using FoundationDB.</p></li>
<li><p>See the <a class="reference internal" href="api-reference.html"><span class="doc">API References</span></a> for detailed API documentation.</p></li>
</ul>
</section>
<section id="appendix-schedulingtutorial-py">
<span id="tutorial-appendix"></span><h2>Appendix: SchedulingTutorial.py</h2>
<p>Heres the code for the scheduling tutorial:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">itertools</span>
<span class="kn">import</span> <span class="nn">traceback</span>
<span class="kn">import</span> <span class="nn">fdb</span>
<span class="kn">import</span> <span class="nn">fdb.tuple</span>
<span class="n">fdb</span><span class="o">.</span><span class="n">api_version</span><span class="p">(</span><span class="mi">730</span><span class="p">)</span>
<span class="c1">####################################</span>
<span class="c1">## Initialization ##</span>
<span class="c1">####################################</span>
<span class="c1"># Data model:</span>
<span class="c1"># (&#39;attends&#39;, student, class) = &#39;&#39;</span>
<span class="c1"># (&#39;class&#39;, class_name) = seats_left</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
<span class="n">db</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">set_transaction_timeout</span><span class="p">(</span><span class="mi">60000</span><span class="p">)</span> <span class="c1"># 60,000 ms = 1 minute</span>
<span class="n">db</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">set_transaction_retry_limit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="n">scheduling</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">directory</span><span class="o">.</span><span class="n">create_or_open</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="p">(</span><span class="s1">&#39;scheduling&#39;</span><span class="p">,))</span>
<span class="n">course</span> <span class="o">=</span> <span class="n">scheduling</span><span class="p">[</span><span class="s1">&#39;class&#39;</span><span class="p">]</span>
<span class="n">attends</span> <span class="o">=</span> <span class="n">scheduling</span><span class="p">[</span><span class="s1">&#39;attends&#39;</span><span class="p">]</span>
<span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">add_class</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="mi">100</span><span class="p">,))</span>
<span class="c1"># Generate 1,620 classes like &#39;9:00 chem for dummies&#39;</span>
<span class="n">levels</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;intro&#39;</span><span class="p">,</span> <span class="s1">&#39;for dummies&#39;</span><span class="p">,</span> <span class="s1">&#39;remedial&#39;</span><span class="p">,</span> <span class="s1">&#39;101&#39;</span><span class="p">,</span>
<span class="s1">&#39;201&#39;</span><span class="p">,</span> <span class="s1">&#39;301&#39;</span><span class="p">,</span> <span class="s1">&#39;mastery&#39;</span><span class="p">,</span> <span class="s1">&#39;lab&#39;</span><span class="p">,</span> <span class="s1">&#39;seminar&#39;</span><span class="p">]</span>
<span class="n">types</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;chem&#39;</span><span class="p">,</span> <span class="s1">&#39;bio&#39;</span><span class="p">,</span> <span class="s1">&#39;cs&#39;</span><span class="p">,</span> <span class="s1">&#39;geometry&#39;</span><span class="p">,</span> <span class="s1">&#39;calc&#39;</span><span class="p">,</span>
<span class="s1">&#39;alg&#39;</span><span class="p">,</span> <span class="s1">&#39;film&#39;</span><span class="p">,</span> <span class="s1">&#39;music&#39;</span><span class="p">,</span> <span class="s1">&#39;art&#39;</span><span class="p">,</span> <span class="s1">&#39;dance&#39;</span><span class="p">]</span>
<span class="n">times</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;:00&#39;</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">20</span><span class="p">)]</span>
<span class="n">class_combos</span> <span class="o">=</span> <span class="n">itertools</span><span class="o">.</span><span class="n">product</span><span class="p">(</span><span class="n">times</span><span class="p">,</span> <span class="n">types</span><span class="p">,</span> <span class="n">levels</span><span class="p">)</span>
<span class="n">class_names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">tup</span><span class="p">)</span> <span class="k">for</span> <span class="n">tup</span> <span class="ow">in</span> <span class="n">class_combos</span><span class="p">]</span>
<span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">tr</span><span class="p">):</span>
<span class="k">del</span> <span class="n">tr</span><span class="p">[</span><span class="n">scheduling</span><span class="o">.</span><span class="n">range</span><span class="p">(())]</span> <span class="c1"># Clear the directory</span>
<span class="k">for</span> <span class="n">class_name</span> <span class="ow">in</span> <span class="n">class_names</span><span class="p">:</span>
<span class="n">add_class</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">class_name</span><span class="p">)</span>
<span class="c1">####################################</span>
<span class="c1">## Class Scheduling Functions ##</span>
<span class="c1">####################################</span>
<span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">available_classes</span><span class="p">(</span><span class="n">tr</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">k</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">range</span><span class="p">(())]</span>
<span class="k">if</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">v</span><span class="p">)[</span><span class="mi">0</span><span class="p">]]</span>
<span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">signup</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="k">if</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span><span class="o">.</span><span class="n">present</span><span class="p">():</span> <span class="k">return</span> <span class="c1"># already signed up</span>
<span class="n">seats_left</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))])[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">seats_left</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">&#39;No remaining seats&#39;</span><span class="p">)</span>
<span class="n">classes</span> <span class="o">=</span> <span class="n">tr</span><span class="p">[</span><span class="n">attends</span><span class="o">.</span><span class="n">range</span><span class="p">((</span><span class="n">s</span><span class="p">,))]</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">classes</span><span class="p">))</span> <span class="o">==</span> <span class="mi">5</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">&#39;Too many classes&#39;</span><span class="p">)</span>
<span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">seats_left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,))</span>
<span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;&#39;</span>
<span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">drop</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="n">rec</span> <span class="o">=</span> <span class="n">attends</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">s</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span><span class="o">.</span><span class="n">present</span><span class="p">():</span> <span class="k">return</span> <span class="c1"># not taking this class</span>
<span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))]</span> <span class="o">=</span> <span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">fdb</span><span class="o">.</span><span class="n">tuple</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="n">tr</span><span class="p">[</span><span class="n">course</span><span class="o">.</span><span class="n">pack</span><span class="p">((</span><span class="n">c</span><span class="p">,))])[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,))</span>
<span class="k">del</span> <span class="n">tr</span><span class="p">[</span><span class="n">rec</span><span class="p">]</span>
<span class="nd">@fdb</span><span class="o">.</span><span class="n">transactional</span>
<span class="k">def</span> <span class="nf">switch</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">old_c</span><span class="p">,</span> <span class="n">new_c</span><span class="p">):</span>
<span class="n">drop</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">old_c</span><span class="p">)</span>
<span class="n">signup</span><span class="p">(</span><span class="n">tr</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">new_c</span><span class="p">)</span>
<span class="c1">####################################</span>
<span class="c1">## Testing ##</span>
<span class="c1">####################################</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="k">def</span> <span class="nf">indecisive_student</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">ops</span><span class="p">):</span>
<span class="n">student_ID</span> <span class="o">=</span> <span class="s1">&#39;s</span><span class="si">{:d}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="n">all_classes</span> <span class="o">=</span> <span class="n">class_names</span>
<span class="n">my_classes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">ops</span><span class="p">):</span>
<span class="n">class_count</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">my_classes</span><span class="p">)</span>
<span class="n">moods</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="n">class_count</span><span class="p">:</span> <span class="n">moods</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;drop&#39;</span><span class="p">,</span> <span class="s1">&#39;switch&#39;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">class_count</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">:</span> <span class="n">moods</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;add&#39;</span><span class="p">)</span>
<span class="n">mood</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">moods</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">all_classes</span><span class="p">:</span>
<span class="n">all_classes</span> <span class="o">=</span> <span class="n">available_classes</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
<span class="k">if</span> <span class="n">mood</span> <span class="o">==</span> <span class="s1">&#39;add&#39;</span><span class="p">:</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">all_classes</span><span class="p">)</span>
<span class="n">signup</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">student_ID</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="n">my_classes</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">mood</span> <span class="o">==</span> <span class="s1">&#39;drop&#39;</span><span class="p">:</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">my_classes</span><span class="p">)</span>
<span class="n">drop</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">student_ID</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="n">my_classes</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">mood</span> <span class="o">==</span> <span class="s1">&#39;switch&#39;</span><span class="p">:</span>
<span class="n">old_c</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">my_classes</span><span class="p">)</span>
<span class="n">new_c</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">all_classes</span><span class="p">)</span>
<span class="n">switch</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">student_ID</span><span class="p">,</span> <span class="n">old_c</span><span class="p">,</span> <span class="n">new_c</span><span class="p">)</span>
<span class="n">my_classes</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">old_c</span><span class="p">)</span>
<span class="n">my_classes</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_c</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Need to recheck available classes.&quot;</span><span class="p">)</span>
<span class="n">all_classes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">students</span><span class="p">,</span> <span class="n">ops_per_student</span><span class="p">):</span>
<span class="n">threads</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">indecisive_student</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">ops_per_student</span><span class="p">))</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">students</span><span class="p">)]</span>
<span class="k">for</span> <span class="n">thr</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span> <span class="n">thr</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">for</span> <span class="n">thr</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span> <span class="n">thr</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Ran </span><span class="si">{}</span><span class="s2"> transactions&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">students</span> <span class="o">*</span> <span class="n">ops_per_student</span><span class="p">))</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">init</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;initialized&quot;</span><span class="p">)</span>
<span class="n">run</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</pre></div>
</div>
</section>
</section>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<p class="pull-right">
<a href="#">Back to top</a>
<br/>
<div id="sourcelink">
<a href="_sources/class-scheduling.rst.txt"
rel="nofollow">Source</a>
</div>
</p>
<p>
&copy; Copyright 2013-2022 Apple, Inc and the FoundationDB project authors.<br/>
Last updated on Nov 20, 2024.<br/>
</p>
</div>
</footer>
</body>
</html>