fluo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ktur...@apache.org
Subject [3/5] incubator-fluo-website git commit: added more pages and automated index generation
Date Mon, 10 Oct 2016 19:13:29 GMT
added more pages and automated index generation


Project: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/commit/e3415fdb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/tree/e3415fdb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/diff/e3415fdb

Branch: refs/heads/gh-pages
Commit: e3415fdb9e4b703b5bb0c0b6886a2acd4f20b57e
Parents: b8786f3
Author: Keith Turner <kturner@apache.org>
Authored: Wed Sep 28 18:17:41 2016 -0400
Committer: Keith Turner <keith@deenlo.com>
Committed: Mon Oct 10 19:09:48 2016 -0400

----------------------------------------------------------------------
 Gemfile.lock                        |   3 -
 _config.yml                         |   4 +-
 _data/tour.yml                      |  28 +++
 _layouts/tour.html                  |  49 +++-
 pages/tour.md                       |  10 -
 tour/application-configuration.md   | 168 +++++++++++++
 tour/architecture.md                |   6 +-
 tour/basic-read-write.md            |  39 +--
 tour/collision-code.md              |  41 ++++
 tour/collisions.md                  |  17 ++
 tour/data-model.md                  |  19 +-
 tour/data-pojos.md                  |  21 ++
 tour/exercise-1.md                  | 405 +++++++++++++++++++++++++++++++
 tour/index.md                       |  29 ++-
 tour/loader-executer.md             |  24 ++
 tour/mem-self-ntfy-code.md          | 117 +++++++++
 tour/mem-self-ntfy.md               |  19 ++
 tour/multi-get.md                   | 131 ++++++++++
 tour/observer_example.md            |  80 ++++++
 tour/observers.md                   |  27 +++
 tour/pg0-intro.md                   |   6 -
 tour/pg5-snapshot-isolation.md      |  20 --
 tour/pg6-snapshot-isolation-code.md |  40 ---
 tour/recipes.md                     |   6 +
 tour/row-locking.md                 |  78 ++++++
 tour/scanning-code.md               |  76 ++++++
 tour/scanning.md                    |  36 +++
 tour/snapshot-isolation-code.md     |  41 ++++
 tour/snapshot-isolation.md          |  20 ++
 tour/tx-logging.md                  |  44 ++++
 tour/weak-code.md                   |  75 ++++++
 tour/weak-notifications.md          |  26 ++
 tour/write-skew-code.md             |  44 ++++
 tour/write-skew.md                  |  31 +++
 tour/writing-code.md                |  26 +-
 35 files changed, 1675 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/Gemfile.lock
----------------------------------------------------------------------
diff --git a/Gemfile.lock b/Gemfile.lock
index a9dd8b1..9d8255a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -125,6 +125,3 @@ PLATFORMS
 
 DEPENDENCIES
   github-pages (= 89)
-
-BUNDLED WITH
-   1.11.2

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/_config.yml
----------------------------------------------------------------------
diff --git a/_config.yml b/_config.yml
index 148eca9..9fd3fc3 100644
--- a/_config.yml
+++ b/_config.yml
@@ -41,6 +41,7 @@ defaults:
       type: "pages"
     values:
       layout: "tour"
+      permalink: "/tour/:basename/"
 
 # Number of posts displayed on the home page.
 num_home_posts: 5
@@ -54,9 +55,6 @@ latest_fluo_release_date: "January 12, 2016"
 latest_recipes_release: "1.0.0-beta-2"
 latest_recipes_release_date: "March 29, 2016"
 
-# Fluo tour settings
-tour_pages: 4
-
 whitelist: [jekyll-redirect-from]
 gems: [jekyll-redirect-from]
 

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/_data/tour.yml
----------------------------------------------------------------------
diff --git a/_data/tour.yml b/_data/tour.yml
new file mode 100644
index 0000000..1f22c44
--- /dev/null
+++ b/_data/tour.yml
@@ -0,0 +1,28 @@
+docs:
+ - data-model
+ - architecture
+ - writing-code
+ - basic-read-write
+ - data-pojos
+ - snapshot-isolation
+ - snapshot-isolation-code
+ - collisions
+ - collision-code
+ - tx-logging
+ - write-skew
+ - write-skew-code
+ - scanning
+ - scanning-code
+ - multi-get
+ - loader-executer
+ - observers
+ - observer_example
+ - exercise-1
+ - row-locking
+ - weak-notifications
+ - weak-code
+ - application-configuration
+ - mem-self-ntfy
+ - mem-self-ntfy-code
+ - recipes
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/_layouts/tour.html
----------------------------------------------------------------------
diff --git a/_layouts/tour.html b/_layouts/tour.html
index 061e0fe..485f2b9 100644
--- a/_layouts/tour.html
+++ b/_layouts/tour.html
@@ -1,25 +1,56 @@
 ---
 layout: default
 ---
+{% assign tour_pages = site.data.tour.docs %}  
+{% for p in tour_pages %}
+  {% assign doc_url = p | prepend: '/tour/' | append: '/' %}
+  {% if doc_url == page.url %}
+    {% assign tour_num = forloop.index %}
+    {% if forloop.first %}
+      {% assign previous = -1 %}
+    {% else %}
+      {% assign previous = forloop.index0 | minus: 1 %}
+      {% assign previous_page = tour_pages[previous] | prepend:"/tour/" | append:"/" %}
+    {% endif %}
+    {% if forloop.last %}
+      {% assign next = 0 %}
+    {% else %}
+      {% assign next = forloop.index0 | plus: 1 %}
+      {% assign next_page = tour_pages[next] | prepend:"/tour/" | append:"/" %}
+    {% endif %}
+  {% endif %}
+{% endfor %}
+
 <div id="tour-header">
   <h2><a href="/tour/">Fluo Tour</a>: {{ page.title }}</h2>
-  <p class="text-muted">Tour page {{ page.tour_num }} of {{ site.tour_pages }}</p>
+  <p class="text-muted">Tour page {{ tour_num }} of {{ tour_pages.size }}</p>
 </div>
 <div id="tour-content">
   {{ content }}
 </div>
 
-<div>
-  {% assign prev = page.tour_num | minus: 1 %}
-  {% assign next = page.tour_num | plus: 1 %}
+<script>
+document.body.onkeyup = function(e){
+{% if previous >= 0 %}
+if (e.keyCode == '37') { window.location = '{{previous_page}}'; }
+{% endif %}
+
+{% if next > 0 %}
+if (e.keyCode == '39') { window.location = '{{next_page}}'; }
+{% endif %}
+};
+</script>
+
+<div class="text-center">
+ 
   <h2> 
-    {% if prev > 0 %}
-    <a href="/tour/{{ prev }}/">&lt;</a> 
+    {% if previous >= 0 %}
+    <a href="{{ previous_page }}">&lt;</a> 
     {% endif %}
 
-    {{ page.tour_num }} / {{ site.tour_pages }} 
-    {% if next <= site.tour_pages %}
-    <a href="/tour/{{ next }}/">&gt;</a> 
+    {{ tour_num }} / {{ tour_pages.size }} 
+    {% if next > 0 %}
+    <a href="{{ next_page }}">&gt;</a> 
     {% endif %}
   </h2>
 </div>

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/pages/tour.md
----------------------------------------------------------------------
diff --git a/pages/tour.md b/pages/tour.md
deleted file mode 100644
index 24e24e7..0000000
--- a/pages/tour.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-layout: page
-title: Apache Fluo Tour
-permalink: /tour/
----
-
-Shortly after Fluo 1.0.0-incubating is released, a step by step introduction with hands on examples will be placed here.  For now this page is a place holder so that the documentation that will ship with Fluo can be modified to point here before release.
-
-
-

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/application-configuration.md
----------------------------------------------------------------------
diff --git a/tour/application-configuration.md b/tour/application-configuration.md
new file mode 100644
index 0000000..2b39281
--- /dev/null
+++ b/tour/application-configuration.md
@@ -0,0 +1,168 @@
+---
+title: Application Configuration
+---
+
+Fluo applications are distributed applications where code is running on many separate machines.
+Getting configuration to these distributed processes can be tricky and cumbersome.  Fluo provides
+two simple mechanisms to assists with this: application configuration and observer configuration.
+This configuration data is stored in zookeeper when an application is initialized.  After
+initialization any Fluo client or Observer can access the configuration.
+
+## Application Configuration
+
+To use application configuration, set properties with the prefix `fluo.app` in your configuration
+file before initialization.  Alternatively use [FluoConfiguration.getAppConfiguration()][fcogac] to
+set these properties programmatically.  After fluo is initialized this information can be accessed
+anywhere by calling [FluoClient.getAppConfiguration()][fclgac],
+[Observer.Context.getAppConfigurtaion()][ocgac], or [Loader.Context.getAppConfiguration()][lcgac].
+
+The following is a simple example of using application config.   This example sets some application
+config before initialization.  After initialization the configuration is accessed via
+FluoConfiguration.
+
+```java
+  private static void preInit(FluoConfiguration fluoConfig) {
+    SimpleConfiguration appConfig = fluoConfig.getAppConfiguration();
+    appConfig.setProperty("exporterClass", "com.foo.MysqlExporter");
+    appConfig.setProperty("exporterDB", "db1");
+    appConfig.setProperty("exporterTable", "table5");
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    SimpleConfiguration appConfig = client.getAppConfiguration();
+    System.out.println(appConfig.getString("exporterClass"));
+    System.out.println(appConfig.getString("exporterDB"));
+    System.out.println(appConfig.getString("exporterTable"));
+  }
+```
+
+The code above prints out the following.
+
+```
+com.foo.MysqlExporter
+db1
+table5
+```
+
+## Observer Configuration
+
+If you want instances of an Observer to behave differently and share code, one way to accomplish
+this is with per observer configuration.  When setting up an observer call one of the
+[ObserverSpecification][ospec] methods that takes configuration.  When an observer is initalized it
+can access this configuration by calling [Observer.Context.getObserverConfiguration()][ocgp].
+
+The code below shows an example of setting configuration for an Observer.  This example simulates an
+observer that can export rows to a mysql table. The example configures two instances of an observer
+using the same class with different configuration.  Even though the observers use the same class, the
+two instances must observe different columns.  Thats why the code derives the observed column based
+on the observer configuration.  Notice the mysql database is obtained from application configuration by
+the observer and the table is obtained from observer configuration.
+
+```java
+package ft;
+
+import org.apache.fluo.api.client.TransactionBase;
+import org.apache.fluo.api.client.scanner.CellScanner;
+import org.apache.fluo.api.data.Bytes;
+import org.apache.fluo.api.data.Column;
+import org.apache.fluo.api.data.RowColumnValue;
+import org.apache.fluo.api.data.Span;
+import org.apache.fluo.api.observer.Observer;
+
+public class MysqlExportObserver implements Observer {
+
+  private String exportDB;
+  private String exportTable;
+
+  @Override
+  public void close() {}
+
+  @Override
+  public ObservedColumn getObservedColumn() {
+    Column col = new Column("ET", exportTable);
+    return new ObservedColumn(col, NotificationType.WEAK);
+  }
+
+  @Override
+  public void init(Context ctx) throws Exception {
+    exportDB = ctx.getAppConfiguration().getString("exportDB");
+    exportTable = ctx.getObserverConfiguration().getString("exportTable");
+  }
+
+  @Override
+  public void process(TransactionBase tx, Bytes row, Column col) throws Exception {
+    CellScanner scanner = tx.scanner().over(Span.exact(row)).build();
+
+    for (RowColumnValue rcv : scanner) {
+      System.out.printf("Exporting val:%s from row:%s to db/table:%s/%s\n", rcv.getsValue(), row,
+          exportDB, exportTable);
+      tx.delete(rcv.getRow(), rcv.getColumn());
+    }
+  }
+}
+```
+
+The following code initializes two observers using the same class with different configuration.  It
+also sets application configuration that is used by the observers.  The code then writes some data
+and notifies the two observers which process the data.
+
+```java
+  private static void preInit(FluoConfiguration fluoConfig) {
+    SimpleConfiguration appConfig = fluoConfig.getAppConfiguration();
+    appConfig.setProperty("exportDB", "db1");
+
+    ObserverSpecification observer1 = new ObserverSpecification(MysqlExportObserver.class.getName(),
+        Collections.singletonMap("exportTable", "table9"));
+
+    ObserverSpecification observer2 = new ObserverSpecification(MysqlExportObserver.class.getName(),
+        Collections.singletonMap("exportTable", "table3"));
+
+    fluoConfig.addObserver(observer1);
+    fluoConfig.addObserver(observer2);
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    try (Transaction tx = client.newTransaction()) {
+      tx.set("e:99", new Column("export", "data1"), "222");
+      tx.set("e:99", new Column("export", "data2"), "444");
+      tx.set("e:99", new Column("export", "data3"), "555");
+
+      tx.setWeakNotification("e:99", new Column("ET", "table3"));
+
+      tx.commit();
+    }
+
+
+    try (Transaction tx = client.newTransaction()) {
+      tx.set("e:98", new Column("export", "data1"), "777");
+      tx.set("e:98", new Column("export", "data2"), "888");
+      tx.set("e:98", new Column("export", "data3"), "999");
+
+      tx.setWeakNotification("e:98", new Column("ET", "table9"));
+
+      tx.commit();
+    }
+
+    mini.waitForObservers();
+  }
+```
+
+Running the code above prints the following.
+
+```
+Exporting val:222 from row:e:99 to db/table:db1/table3
+Exporting val:777 from row:e:98 to db/table:db1/table9
+Exporting val:444 from row:e:99 to db/table:db1/table3
+Exporting val:888 from row:e:98 to db/table:db1/table9
+Exporting val:555 from row:e:99 to db/table:db1/table3
+Exporting val:999 from row:e:98 to db/table:db1/table9
+```
+
+[fcogac]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/config/FluoConfiguration.html#getAppConfiguration--
+[fclgac]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/FluoClient.html#getAppConfiguration--
+[ocgac]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/observer/Observer.Context.html#getAppConfiguration--
+[lcgac]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/Loader.Context.html#getAppConfiguration--
+[ospec]: /apidocs/fluo/{{ site.latest_fluo_release}}/org/apache/fluo/api/config/ObserverSpecification.html
+[ocgp]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/observer/Observer.Context.html#getObserverConfiguration--
+
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/architecture.md
----------------------------------------------------------------------
diff --git a/tour/architecture.md b/tour/architecture.md
index bba8446..48ed123 100644
--- a/tour/architecture.md
+++ b/tour/architecture.md
@@ -1,7 +1,7 @@
 ---
 title: Architecture
-tour_num: 2
-permalink: /tour/2/
 ---
 
-TODO picture
+An [overview] of the Fluo Architecture can be found in Fluo's documentation.
+
+[overview]: /docs/fluo/{{ site.latest_fluo_release }}/architecture/

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/basic-read-write.md
----------------------------------------------------------------------
diff --git a/tour/basic-read-write.md b/tour/basic-read-write.md
index a58f13b..c476e1d 100644
--- a/tour/basic-read-write.md
+++ b/tour/basic-read-write.md
@@ -1,13 +1,10 @@
 ---
 title: Read and Write Data
-tour_num: 4
-permalink: /tour/4/
 ---
 
-The following example shows basic code for writing data using Fluo and then
-reading it back out.  To run this code modify `src/main/java/ft/Main.java` in
-[Fluo Tour git repository][1] and run it following the instructions on the
-repository.
+The following example shows basic code for writing data using Fluo and then reading it back out.  To
+run this code modify `src/main/java/ft/Main.java` in the [Fluo Tour git repository][1] and run it
+following the instructions on the repository.
 
 
 ```java
@@ -29,16 +26,20 @@ repository.
   }
 ```
 
-In the example above a Transaction is created in a try with resources block.  A
-Transaction can read and write data.  It can only read data that was committed
-before it started.  Data set on a transaction will only be written when
-`commit()` is called.
-  
-A Snapshot is created to read the data previously written by the Transaction.
-Snapshots only allow reading data that was commited before the snapshot was
-created.  The code calls `gets()` which is a convience method that returns a
-Java string.  Internally Fluo only deals with bytes and its `get()` method
-returns bytes.   The Fluo methods that take and return Strings assume UTF-8
-encoding when converting to bytes.
-
-[1]:https://github.com/keith-turner/fluo-tour
+In the example above a Transaction is created in a try-with-resources block.  A Transaction can read
+and write data.  It can only read data that was committed before it started.  Data set on a
+transaction will only be written when `commit()` is called.
+
+A Snapshot is created to read the data previously written by the Transaction.  Snapshots only see
+data committed before the snapshot was created.  The code calls [gets(CharSequence, Column)][gets] which
+is a convenience method that works with strings.  Internally Fluo only deals with bytes and its
+[get(Bytes, Column)][get] method returns [Bytes].   The Fluo methods that take and return Strings
+assume UTF-8 encoding when converting to bytes.
+
+Transactions and snapshots allocate resources and therefore have `close()` methods.  The
+try-with-resources block will automatically call `close()`, even when exceptions occur.
+
+[1]: https://github.com/apache/incubator-fluo-website/tree/fluo-tour
+[get]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#get-org.apache.fluo.api.data.Bytes-org.apache.fluo.api.data.Column-
+[gets]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#gets-java.lang.CharSequence-org.apache.fluo.api.data.Column-
+[Bytes]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/data/Bytes.html

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/collision-code.md
----------------------------------------------------------------------
diff --git a/tour/collision-code.md b/tour/collision-code.md
new file mode 100644
index 0000000..9df2ff2
--- /dev/null
+++ b/tour/collision-code.md
@@ -0,0 +1,41 @@
+---
+title: Collision code
+---
+
+```java
+  private static void excercise(MiniFluo mini, FluoClient client) {
+
+    String row = "kerbalnaut0001";
+    Column lName = new Column("name", "last");
+
+    try(Transaction tx1 = client.newTransaction()) {
+      tx1.set(row, lName, "Kerma");
+      tx1.commit();
+    }
+
+    try(Transaction tx2 = client.newTransaction(); Transaction tx3 = client.newTransaction()) {
+
+      tx2.set(row, lName, tx2.gets(row, lName)+"n");
+      tx3.set(row, lName, tx3.gets(row, lName)+"N");
+
+      tx2.commit();
+      try{
+        tx3.commit();
+      } catch (Exception e) {
+        System.out.println("tx3 commit exception : " +e);
+      }
+    }
+
+    try(Snapshot s1 = client.newSnapshot()) {
+      System.out.println(s1.gets(row, lName));
+    }
+  }
+```
+
+The code above prints :
+
+```
+tx3 commit exception message : org.apache.fluo.api.exceptions.CommitException
+Kerman
+```
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/collisions.md
----------------------------------------------------------------------
diff --git a/tour/collisions.md b/tour/collisions.md
new file mode 100644
index 0000000..425d7cf
--- /dev/null
+++ b/tour/collisions.md
@@ -0,0 +1,17 @@
+---
+title: Collisions
+---
+
+When two transactions overlap and attempt to modify the same data, one of them
+will fail.  Try writing code to do the following which will create a collision.
+
+ * **Create transaction** *tx1*
+ * **Using** *tx1* **set** *kerbalnaut0001:name:last* **to** *Kerma*
+ * **Commit** *tx1*
+ * **Create transaction** *tx2*
+ * **Create transaction** *tx3*
+ * **Using** *tx2* **append** *n* **to the value of** *kerbalnaut0001:name:last*
+ * **Using** *tx3* **append** *N* **to the value of** *kerbalnaut0001:name:last*
+ * **Commit** *tx2*
+ * **Commit** *tx3* **wrapping with try/catch that prints Exception**
+ * **Create snapshot and print** *kerbalnaut0001:name:last*

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/data-model.md
----------------------------------------------------------------------
diff --git a/tour/data-model.md b/tour/data-model.md
index f0d1f6a..2425230 100644
--- a/tour/data-model.md
+++ b/tour/data-model.md
@@ -1,7 +1,5 @@
 ---
 title: Data Model
-tour_num: 1
-permalink: /tour/1/
 ---
 
 Fluo uses Accumulo's data model which is based on the BigTable data model.
@@ -17,12 +15,23 @@ column qualifier, and column visibility.
    within each node on the cluster.  The data model has locality groups which
    store column families in physically separate partitions on each node.  If
    locality groups are configured, then it allows scanning a subset of rows column
-   families much more quickly. 
+   families much more quickly.
  * **Column Qualifier** : This portion of the key is used to define arbitrary
-   columns with a column family.
+   columns within a column family.
  * **Column Visibility** : In Accumulo this portion of the key is used to
    control access to data.
 
 This data model is schema-less, there is no need to predefine columns.  One
-thing thats defined in the Accumulo data modes thats not available in Fluo is
+thing that's defined in the Accumulo data modes that's not available in Fluo is
 timestamps.  When using Fluo the timestamp portion of the key is not available.
+
+In the subsequent pages of the tour the shorthand *\<row\>:\<family\>:\<qualifier\>*
+will be used.  For example, the following ...
+
+```
+row       = kerbalnaut001
+family    = name
+qualifier = last
+```
+
+... would be written as *kerbalnaut001:name:last*.

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/data-pojos.md
----------------------------------------------------------------------
diff --git a/tour/data-pojos.md b/tour/data-pojos.md
new file mode 100644
index 0000000..12720a3
--- /dev/null
+++ b/tour/data-pojos.md
@@ -0,0 +1,21 @@
+---
+title: Basic Data Types
+---
+
+Fluo has a few simple POJOs that are used throughout the API.  These classes
+are found in [org.apache.fluo.api.data][data-pkg].  All of these type are
+immutable. Except for Span, all of the types are Comparable and have good hash
+code and equals implementations.  These types work with [Bytes] and java
+Strings.  Internally, Fluo only works with bytes.  All API methods that deal
+with String will convert back and forth to and from bytes using UTF-8.
+
+[Bytes] is modeled after Java String.  Its an immutable wrapper around `byte[]`
+like String is an immutable wrapper around `char[]`.  To make building [Bytes]
+more efficient and easier, there is a reusable [BytesBuilder] thats modeled
+after StringBuilder.  Call [Bytes.builder()][nb] to create a new [BytesBuilder].
+
+[data-pkg]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/data/package-summary.html
+[Bytes]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/data/Bytes.html
+[BytesBuilder]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/data/Bytes.BytesBuilder.html
+[nb]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/data/Bytes.html#builder--
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/exercise-1.md
----------------------------------------------------------------------
diff --git a/tour/exercise-1.md b/tour/exercise-1.md
new file mode 100644
index 0000000..3ec2a05
--- /dev/null
+++ b/tour/exercise-1.md
@@ -0,0 +1,405 @@
+---
+title: Word count Exercise
+---
+
+This Exercise will show you how to create a simple system that computes word
+counts for unique documents.   This system should do the following.
+
+ * Deduplicate content based on hash
+ * Count how many URIs reference content
+ * For the unique words in content, update global word counts.
+ * When new content is added increment the global counts.
+ * When content is no longer referenced by any URIs, decrement the global word counts and delete
+   that content.
+ * Partition different types of data using row prefixes.  Use *u:* for URIs, use *d:* for document
+   content, and use *w:* for word counts.
+
+A skeleton of the code needed to implement this exercise is provided below along with some simple
+test data.
+
+## Part 1 : Loading data.
+
+The class below is a simple POJO for documents.
+
+```java
+package ft;
+
+import com.google.common.hash.Hashing;
+
+public class Document {
+  public final String uri;
+  public final String content;
+
+  public Document(String uri, String content) {
+    this.uri = uri;
+    this.content = content;
+  }
+
+  public String hash() {
+    //use short prefix of hash for example
+    return Hashing.sha1().hashString(content).toString().substring(0, 7);
+  }
+}
+```
+
+The following code loads documents into Fluo.  It should do the following :
+
+ * Keep track of the current hash associated with a URI.
+ * Deduplicate content based on hash
+ * Reference count how many URIs point to content.  Track this information in a column named
+   *doc:refc* with a row based on the hash.
+ * Track the status of whether content is referenced or unreferenced in a column named *doc:refs*.
+   Note *refs* is short for reference status.   When the reference count for content is 0 this
+   columns value should be *unreferenced*.  When the reference count is greater than 0, the
+   *doc:refs* columns value should be *referenced*.  In a later example, an Observer will watch this
+   column.
+ * Track the content associated with a hash using the *doc:content* column.
+
+Some of this is implemented below, but not all. The parts that are not done have TODOs.
+
+```java
+package ft;
+
+import org.apache.fluo.api.client.Loader;
+import org.apache.fluo.api.client.TransactionBase;
+import org.apache.fluo.api.data.Column;
+
+public class DocLoader implements Loader {
+
+  private final Document doc;
+
+  public static final Column HASH_COL = new Column("uri", "hash");
+  public static final Column REF_COUNT_COL = new Column("doc", "refc");
+  public static final Column REF_STATUS_COL = new Column("doc", "refs");
+  public static final Column CONTENT_COL = new Column("doc", "content");
+
+  public DocLoader(Document doc) {
+    this.doc = doc;
+  }
+
+  @Override
+  public void load(TransactionBase tx, Context context) throws Exception {
+    String newHash = doc.hash();
+    String oldHash = tx.gets("u:" + doc.uri, HASH_COL);
+
+    // TODO check if uri already has the same content hash.  If so, then nothing to do.
+
+    // TODO set the new hash associated with the URI
+
+    if (oldHash != null) {
+      // TODO decrement the reference count at row "d:"+oldHash
+      // TODO set REF_STATUS_COL to "unreferenced" when the reference count goes from 1 to 0. Do
+      // this for row "d:"+oldHash
+    }
+
+    // TODO increment the reference count for the newHash content.
+    // TODO add the new content when the reference count does not exists
+    // TODO set REF_STATUS_COL to "referenced" when the reference count for the new content goes
+    // from 0 to 1.  Do this for row "d:"+newHash
+  }
+}
+```
+
+Add the following to the ft.Main class.
+
+```java
+  // some test data
+  private static Document[] docs1 = new Document[] {
+      new Document("http://news.com/a23",
+          "Jebediah orbits Mun for 35 days.  No power, forgot solar panels."),
+      new Document("http://news.com/a24",
+          "Bill plans to rescue Jebediah after taking tourist to Minimus.")};
+
+  private static Document[] docs2 = new Document[] {new Document("http://oldnews.com/a23",
+      "Jebediah orbits Mun for 35 days.  No power, forgot solar panels.")};
+
+  private static Document[] docs3 = new Document[] {
+      new Document("http://news.com/a23",
+          "Jebediah orbits Mun for 38 days.  No power, forgot solar panels."),
+      new Document("http://news.com/a24",
+          "Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.")};
+
+  /**
+   * Utility method for loading documents and printing out Fluo table after load completes.
+   */
+  private static void loadAndPrint(MiniFluo mini, FluoClient client, Document[] docs) {
+
+    try (LoaderExecutor loaderExecutor = client.newLoaderExecutor()) {
+      for (Document document : docs) {
+        loaderExecutor.execute(new DocLoader(document));
+      }
+    } // this will close loaderExecutor and wait for all load transactions to complete
+
+    //This line is not needed in this step of the excercise.  However the next step will need this
+    //line.
+    mini.waitForObservers();
+
+    System.out.println("**** begin table dump ****");
+    try (Snapshot snap = client.newSnapshot()) {
+      snap.scanner().build().forEach(rcv -> System.out.println("  " + rcv));
+    }
+    System.out.println("**** end table dump ****\n");
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    loadAndPrint(mini, client, docs1);
+    loadAndPrint(mini, client, docs2);
+    loadAndPrint(mini, client, docs3);
+  }
+```
+
+Once the TODOs in the DocLoader class are implemented, running Main should print out the following.
+
+```
+**** begin table dump ****
+  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+  d:a6c4d1f doc refc  1
+  d:a6c4d1f doc refs  referenced
+  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
+  d:cf8ddc0 doc refc  1
+  d:cf8ddc0 doc refs  referenced
+  u:http://news.com/a23 uri hash  a6c4d1f
+  u:http://news.com/a24 uri hash  cf8ddc0
+**** end table dump ****
+
+**** begin table dump ****
+  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+  d:a6c4d1f doc refc  2
+  d:a6c4d1f doc refs  referenced
+  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
+  d:cf8ddc0 doc refc  1
+  d:cf8ddc0 doc refs  referenced
+  u:http://news.com/a23 uri hash  a6c4d1f
+  u:http://news.com/a24 uri hash  cf8ddc0
+  u:http://oldnews.com/a23 uri hash  a6c4d1f
+**** end table dump ****
+
+**** begin table dump ****
+  d:2732ebc doc content  Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.
+  d:2732ebc doc refc  1
+  d:2732ebc doc refs  referenced
+  d:6658252 doc content  Jebediah orbits Mun for 38 days.  No power, forgot solar panels.
+  d:6658252 doc refc  1
+  d:6658252 doc refs  referenced
+  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+  d:a6c4d1f doc refc  1
+  d:a6c4d1f doc refs  referenced
+  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
+  d:cf8ddc0 doc refc  0
+  d:cf8ddc0 doc refs  unreferenced
+  u:http://news.com/a23 uri hash  6658252
+  u:http://news.com/a24 uri hash  2732ebc
+  u:http://oldnews.com/a23 uri hash  a6c4d1f
+**** end table dump ****
+```
+
+## Part 2 : Computing word counts.
+
+Now that you have data loading, create an observer that watches the reference
+status column.  This observer should increment word counts when new content is
+referenced and decrement word counts when content is dereferenced.  The
+observer should also delete the content when its dereferenced.
+
+Make sure you handle the following scenario correctly.
+
+ * content A becomes referenced
+ * content A becomes unreferenced
+ * an observer runs on content A
+
+In this situation word counts were never incremented for content A, so there is
+no need to decrement the word counts.  One way to handle this is to have a
+column that tracks if word counts were incremented.
+
+Below is a skeleton for an observer to compute word counts.
+
+```java
+package ft;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.fluo.api.client.TransactionBase;
+import org.apache.fluo.api.data.Bytes;
+import org.apache.fluo.api.data.Column;
+import org.apache.fluo.api.data.RowColumn;
+import org.apache.fluo.api.observer.AbstractObserver;
+
+public class ContentObserver extends AbstractObserver {
+
+  public static final Column PROCESSED_COL = new Column("doc", "processed");
+  public static final Column WORD_COUNT = new Column("word","docCount");
+
+  /**
+   * Utility method to tokenize the content of a document into unique words.
+   */
+  private Set<String> tokenize(String content) {
+    return new HashSet<String>(Arrays.asList(content.split("[ .!,]+")));
+  }
+
+  /**
+   *  Adds the passed to delta to the values for each word.
+   */
+  private void adjustCounts(TransactionBase tx, int delta, Set<String> words) {
+    // TODO make a single call to get all of the current word counts.  Could use
+    //tx.gets(Collection<RowColumn>)
+
+    // TODO for each word, add delta to the current value and set the new value
+  }
+
+
+  @Override
+  public void process(TransactionBase tx, Bytes brow, Column col) throws Exception {
+
+    String row = brow.toString();
+
+    Map<Column, String> colVals =
+        tx.gets(row, DocLoader.CONTENT_COL, DocLoader.REF_STATUS_COL, PROCESSED_COL);
+
+    String content = colVals.get(DocLoader.CONTENT_COL);
+    String status = colVals.get(DocLoader.REF_STATUS_COL);
+    String processed = colVals.getOrDefault(PROCESSED_COL, "false");
+
+    // TODO if status is referenced and not already processed the adjustCounts by +1 and set
+    // PROCESSED_COL to true
+
+    // TODO is status is unreferenced then delete all columns for content
+    // TODO if status is unreferenced and document was processed, then adjust counts by -1
+  }
+
+
+  @Override
+  public ObservedColumn getObservedColumn() {
+    return new ObservedColumn(DocLoader.REF_STATUS_COL, NotificationType.STRONG);
+  }
+}
+```
+
+Something to think about: why observe the reference status column instead of the reference count
+column?
+
+When you are ready to run the observer, modify the `preInit()` method in `ft.Main` to configure the
+observer as follows.
+
+```java
+  private static void preInit(FluoConfiguration fluoConfig) {
+    fluoConfig.addObserver(new ObserverSpecification(ContentObserver.class.getName()));
+  }
+```
+
+After implementing the Observer, the output of the program should look like the following.
+
+```
+**** begin table dump ****
+  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+  d:a6c4d1f doc processed  true
+  d:a6c4d1f doc refc  1
+  d:a6c4d1f doc refs  referenced
+  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
+  d:cf8ddc0 doc processed  true
+  d:cf8ddc0 doc refc  1
+  d:cf8ddc0 doc refs  referenced
+  u:http://news.com/a23 uri hash  a6c4d1f
+  u:http://news.com/a24 uri hash  cf8ddc0
+  w:35 word docCount  1
+  w:Bill word docCount  1
+  w:Jebediah word docCount  2
+  w:Minimus word docCount  1
+  w:Mun word docCount  1
+  w:No word docCount  1
+  w:after word docCount  1
+  w:days word docCount  1
+  w:for word docCount  1
+  w:forgot word docCount  1
+  w:orbits word docCount  1
+  w:panels word docCount  1
+  w:plans word docCount  1
+  w:power word docCount  1
+  w:rescue word docCount  1
+  w:solar word docCount  1
+  w:taking word docCount  1
+  w:to word docCount  1
+  w:tourist word docCount  1
+**** end table dump ****
+
+**** begin table dump ****
+  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+  d:a6c4d1f doc processed  true
+  d:a6c4d1f doc refc  2
+  d:a6c4d1f doc refs  referenced
+  d:cf8ddc0 doc content  Bill plans to rescue Jebediah after taking tourist to Minimus.
+  d:cf8ddc0 doc processed  true
+  d:cf8ddc0 doc refc  1
+  d:cf8ddc0 doc refs  referenced
+  u:http://news.com/a23 uri hash  a6c4d1f
+  u:http://news.com/a24 uri hash  cf8ddc0
+  u:http://oldnews.com/a23 uri hash  a6c4d1f
+  w:35 word docCount  1
+  w:Bill word docCount  1
+  w:Jebediah word docCount  2
+  w:Minimus word docCount  1
+  w:Mun word docCount  1
+  w:No word docCount  1
+  w:after word docCount  1
+  w:days word docCount  1
+  w:for word docCount  1
+  w:forgot word docCount  1
+  w:orbits word docCount  1
+  w:panels word docCount  1
+  w:plans word docCount  1
+  w:power word docCount  1
+  w:rescue word docCount  1
+  w:solar word docCount  1
+  w:taking word docCount  1
+  w:to word docCount  1
+  w:tourist word docCount  1
+**** end table dump ****
+
+**** begin table dump ****
+  d:2732ebc doc content  Crisis at KSC.  Tourist stuck at Minimus.  Bill forgot solar panels.
+  d:2732ebc doc processed  true
+  d:2732ebc doc refc  1
+  d:2732ebc doc refs  referenced
+  d:6658252 doc content  Jebediah orbits Mun for 38 days.  No power, forgot solar panels.
+  d:6658252 doc processed  true
+  d:6658252 doc refc  1
+  d:6658252 doc refs  referenced
+  d:a6c4d1f doc content  Jebediah orbits Mun for 35 days.  No power, forgot solar panels.
+  d:a6c4d1f doc processed  true
+  d:a6c4d1f doc refc  1
+  d:a6c4d1f doc refs  referenced
+  u:http://news.com/a23 uri hash  6658252
+  u:http://news.com/a24 uri hash  2732ebc
+  u:http://oldnews.com/a23 uri hash  a6c4d1f
+  w:35 word docCount  1
+  w:38 word docCount  1
+  w:Bill word docCount  1
+  w:Crisis word docCount  1
+  w:Jebediah word docCount  2
+  w:KSC word docCount  1
+  w:Minimus word docCount  1
+  w:Mun word docCount  2
+  w:No word docCount  2
+  w:Tourist word docCount  1
+  w:at word docCount  1
+  w:days word docCount  2
+  w:for word docCount  2
+  w:forgot word docCount  3
+  w:orbits word docCount  2
+  w:panels word docCount  3
+  w:power word docCount  2
+  w:solar word docCount  3
+  w:stuck word docCount  1
+**** end table dump ****
+```
+
+
+## Part 3 : Using Fluo Recipes
+
+The way to compute word counts above is very prone to transactional collisions.  One way to avoid
+these collisions would be use the CollisionFreeMap provided in Fluo Recipes.   Currently Fluo
+Recipes is not released, this sections will be updated with more information once it is.
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/index.md
----------------------------------------------------------------------
diff --git a/tour/index.md b/tour/index.md
index dbd1a26..7e30f9b 100644
--- a/tour/index.md
+++ b/tour/index.md
@@ -1,13 +1,30 @@
 ---
 layout: page
 title: Fluo Tour
+permalink: /tour/
 ---
 
-Welcome to the Fluo tour! The tour starts by introducing Fluo's [Data Model](/tour/1/).
+{% assign tour_pages = site.data.tour.docs %}
+{% assign first_url = tour_pages[0] | prepend: '/tour/' | append: '/' %}
+{% assign first_page = site.pages | where:'url',first_url | first %}
 
-We recommend following the tour in order. However, all pages are listed below for review.
+Welcome to the Fluo tour! The tour starts by introducing Fluo's [{{ first_page.title }}]({{ first_url }}).
 
-1. [Data Model](/tour/1/)
-2. [Architecture](/tour/2/)
-3. [Writing Code](/tour/3/)
-4. [Basic Read Write](/tour/4/)
+We recommend following the tour in order. However, all pages are listed below for review.  When on a
+tour page, the left and right keys on the keyboard can be used to navigate.  If you have any
+questions or suggestions while going through the tour, please let us know.  There are multiple
+options for getting in touch : [mailing list, IRC][contact], and [Github Issues][issues].  Any
+thoughts, solutions, etc  related to this tour can also be tweeted using the hashtag
+[#apachefluotour][aft].
+
+
+{% for p in tour_pages %}
+  {% assign doc_url = p | prepend: '/tour/' | append: '/' %}
+  {% assign link_to_page = site.pages | where:'url',doc_url | first %}
+  1. [{{ link_to_page.title }}]({{ doc_url }})
+{% endfor %}
+
+
+[contact]: /getinvolved/
+[issues]: https://github.com/apache/incubator-fluo-website/issues
+[aft]: https://twitter.com/hashtag/apachefluotour

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/loader-executer.md
----------------------------------------------------------------------
diff --git a/tour/loader-executer.md b/tour/loader-executer.md
new file mode 100644
index 0000000..2e5ffb0
--- /dev/null
+++ b/tour/loader-executer.md
@@ -0,0 +1,24 @@
+---
+title: Loader Executor
+---
+
+Fluo provides a simple mechanism to help load data called the [LoaderExecutor][le].  Loading data
+into Fluo requires a transaction.  The LoaderExecutor manages creating , commiting, and retrying
+transactions when collisions occur.  It also runs transactions in multiple threads and batches
+commit processing of separate transactions for efficiency.  FluoConfiguration provides two methods
+for configuring LoaderExecutors [setLoaderQueueSize()][fcqs] and [setLoaderThreads()][fcst].
+
+Objects that implement [Loader] are given to a LoaderExecutor.  The [load()][lm] method will
+eventually be called on these objects at which point the passed in transactions can be used to load
+data.  When `close()` is called on a LoaderExecutor, it waits for all running and queued work to
+finish.
+
+There is no stand alone excercise for the LoaderExecutor.  Hands on experience with it can be
+obtained by completing the [word count exercise](/tour/exercise-1/) that is a few pages later in
+the tour.
+
+[le]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/LoaderExecutor.html
+[Loader]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/Loader.html
+[lm]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/Loader.html#load-org.apache.fluo.api.client.TransactionBase-org.apache.fluo.api.client.Loader.Context-
+[fcqs]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/config/FluoConfiguration.html#setLoaderQueueSize-int-
+[fcst]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/config/FluoConfiguration.html#setLoaderThreads-int-

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/mem-self-ntfy-code.md
----------------------------------------------------------------------
diff --git a/tour/mem-self-ntfy-code.md b/tour/mem-self-ntfy-code.md
new file mode 100644
index 0000000..d464007
--- /dev/null
+++ b/tour/mem-self-ntfy-code.md
@@ -0,0 +1,117 @@
+---
+title: Memory limits and self notify code
+---
+
+```java
+  static Column NC = new Column("ntyf", "sum");
+  static Column TOTAL_COL = new Column("sum", "total");
+  static Column UPDATE_COL = new Column("sum", "update");
+  static Column CONTINUE_COL = new Column("sum", "continue");
+
+  public static class SummingObserver extends AbstractObserver {
+
+    private int maxToProcess;
+
+    @Override
+    public void init(Context context) throws Exception {
+      //made the max amount to process in a single transaction configurable
+      maxToProcess = context.getObserverConfiguration().getInt("maxToProcess", 100);
+    }
+
+    @Override
+    public ObservedColumn getObservedColumn() {
+      return new ObservedColumn(NC, NotificationType.WEAK);
+    }
+
+    @Override
+    public void process(TransactionBase tx, Bytes brow, Column col) throws Exception {
+
+      String row = brow.toString();
+
+      Map<Column, String> colVals = tx.gets(row, TOTAL_COL, CONTINUE_COL);
+
+      int sum = Integer.parseInt(colVals.getOrDefault(TOTAL_COL, "0"));
+      
+      // construct a scan range that uses the continue row
+      String startRow = colVals.getOrDefault(CONTINUE_COL, row + "/");
+      String endRow = row + "/:"; // after the character '9' comes ':'
+      CellScanner scanner = tx.scanner().over(new Span(startRow, true, endRow, false)).build();
+
+      int processed = 0;
+      
+      for (RowColumnValue rcv : scanner) {
+        if (processed >= maxToProcess) {
+          // stop processing and set the continue row
+          tx.set(row, CONTINUE_COL, rcv.getsRow());
+          tx.setWeakNotification(brow, col);
+          break;
+        }
+        sum += Integer.parseInt(rcv.getsValue());
+        tx.delete(rcv.getRow(), rcv.getColumn());
+        processed++;
+      }
+
+      System.out.println("sum : " + sum + "  start: " + startRow + "  processed: " + processed);
+
+      tx.set(row, TOTAL_COL, "" + sum);
+
+      // if did not set the continue column and it exists, then delete it
+      if (processed < maxToProcess && colVals.containsKey(CONTINUE_COL)) {
+        tx.delete(row, CONTINUE_COL);
+        // need to start over at the beginning and see if there is new data before the continue
+        // column
+        tx.setWeakNotification(brow, col);
+      }
+    }
+  }
+
+  private static void preInit(FluoConfiguration fluoConfig) {
+    ObserverSpecification ospec = new ObserverSpecification(SummingObserver.class.getName());
+    ospec.getConfiguration().setProperty("maxToProcess", 500);
+    fluoConfig.addObserver(ospec);
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    try (LoaderExecutor le = client.newLoaderExecutor()) {
+      Random r = new Random(42);
+      for (int i = 0; i < 5000; i++) {
+        // The Loader interface only has one function and can therefore be written as a lambda
+        // below.
+        le.execute((tx, ctx) -> {
+          String row = "counter001/" + String.format("%07d", r.nextInt(10000000));
+          int curVal = Integer.parseInt(tx.gets(row, UPDATE_COL, "0"));
+          tx.set(row, UPDATE_COL, curVal + 1 + "");
+          tx.setWeakNotification("counter001", NC);
+        });
+      }
+    }
+
+    mini.waitForObservers();
+
+    try (Snapshot snap = client.newSnapshot()) {
+      System.out.println("final sum : " + snap.gets("counter001", TOTAL_COL));
+    }
+  }
+```
+
+The code above will print something like the following.
+
+```
+$ mvn -q clean compile exec:java
+Starting MiniFluo ... started.
+sum : 500  start: counter001/  processed: 500
+sum : 891  start: counter001/7945963  processed: 390
+sum : 1391  start: counter001/  processed: 500
+sum : 1891  start: counter001/2938489  processed: 500
+sum : 2391  start: counter001/5210523  processed: 500
+sum : 2892  start: counter001/6912090  processed: 500
+sum : 3392  start: counter001/8410312  processed: 500
+sum : 3398  start: counter001/9991522  processed: 6
+sum : 3898  start: counter001/  processed: 500
+sum : 4398  start: counter001/1824962  processed: 500
+sum : 4899  start: counter001/4076664  processed: 500
+sum : 5000  start: counter001/6993690  processed: 101
+sum : 5000  start: counter001/  processed: 0
+final sum : 5000
+```
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/mem-self-ntfy.md
----------------------------------------------------------------------
diff --git a/tour/mem-self-ntfy.md b/tour/mem-self-ntfy.md
new file mode 100644
index 0000000..12e2694
--- /dev/null
+++ b/tour/mem-self-ntfy.md
@@ -0,0 +1,19 @@
+---
+title: Memory limits and self notify
+---
+
+All modifications made as part of a transaction must fit into memory because the sets and deletes
+are buffered in memory until commit.  If there is more data to process that will fit in memory, one
+way to handle this is to process some data and self notify.
+
+As an exercise try modifying the [weak notification exercise](/tour/weak-notifications/) and
+making it self notify.  Modify the observer such that it does the following :
+
+ * Processes a maximum number of updates.  Could make this configurable using per observer
+   configuration.
+ * When the max is reached : 
+   * Stop processing.
+   * Records the stop row.
+   * Notify self.
+ * Use the row where it previously stopped when creating scan range.
+ * Delete the stop row, if it exists, after scanning to the end of the range.

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/multi-get.md
----------------------------------------------------------------------
diff --git a/tour/multi-get.md b/tour/multi-get.md
new file mode 100644
index 0000000..3a01897
--- /dev/null
+++ b/tour/multi-get.md
@@ -0,0 +1,131 @@
+---
+title: Fetching multiple cells
+---
+
+Each call to get a row column results in a RPC to an Accumulo server.  In the cases where there are
+many row columns to get, Fluo provides more specialized get methods that make less RPC calls.
+
+Below is some example code that illustrates using these methods and shows the performance
+difference.  The example code performs the following task.
+
+ * In a single transaction, generates 100 rows each with 100 columns such that each row has the same
+   columns.  Uses integers for the row and columns names.
+ * Gets 100 columns from a single row in the following ways.  Times each way.
+   * In a loop calls  [gets(CharSequence, Column)][get].
+   * Calls [gets(CharSequence, Set\<Column\>)][getsc] once
+ * Gets 100 columns from 3 rows in the following ways.  Times each way. 
+   * For three rows, loops over 100 columns calling  [gets(CharSequence, Column)][get]
+   * Calls [gets(Collection\<? extends CharSequence\>, Set\<Column\>)][getmc] once
+ * Generates 100 row column pairs, where each pair is a random row and a random column. Gets each
+ * pair in the following ways.  Times each way.  
+   * For each pair calls [gets(CharSequence, Column)][get]
+   * Calls [gets(Collection\<RowColumn\>)][getrc] once
+
+Below is the code to perform the task mentioned above.
+
+```java
+  private static void excercise(MiniFluo mini, FluoClient client) {
+
+    Set<Column> columns = new LinkedHashSet<>();
+
+    for(int c = 0; c < 100; c++) {
+      columns.add(new Column("f", String.format("q%04d", c)));
+    }
+
+    try(Transaction tx = client.newTransaction()) {
+      int value = 0;
+      for(int r = 0; r < 100; r++) {
+        String row = String.format("r%04d", r);
+        for (Column column : columns) {
+          tx.set(row, column, value+"");
+          value++;
+        }
+      }
+
+      tx.commit();
+    }
+
+    //fetch multiple columns from a single row
+    try(Snapshot snap = client.newSnapshot()) {
+      String row = String.format("r%04d", 42);
+
+      long t1 = System.currentTimeMillis();
+
+      for (Column column : columns) {
+        snap.gets(row, column);
+      }
+
+      long t2 = System.currentTimeMillis();
+
+      snap.gets(row, columns);
+
+      long t3 = System.currentTimeMillis();
+
+      System.out.printf("test1 time 1:%d  time2:%d\n",(t2-t1),(t3-t2));
+    }
+
+
+    //fetch the same columns from multiple rows
+    try(Snapshot snap = client.newSnapshot()) {
+      List<String> rows = Arrays.asList(String.format("r%04d", 42),
+                                        String.format("r%04d", 21),
+                                        String.format("r%04d", 84));
+
+      long t1 = System.currentTimeMillis();
+
+      for (String row : rows) {
+        for (Column column : columns) {
+          snap.gets(row, column);
+        }
+      }
+
+      long t2 = System.currentTimeMillis();
+
+      snap.gets(rows, columns);
+
+      long t3 = System.currentTimeMillis();
+
+      System.out.printf("test2 time 1:%d  time2:%d\n",(t2-t1),(t3-t2));
+    }
+
+    //fetch different columns from different rows
+    try(Snapshot snap = client.newSnapshot()) {
+      Random rand = new Random();
+      //generate the row columns to fetch
+      List<RowColumn> rowcols = new ArrayList<>();
+      for(int i = 0; i < 100; i++) {
+        String row = String.format("r%04d", rand.nextInt(100));
+        Column col = new Column("f", String.format("q%04d", rand.nextInt(100)));
+        rowcols.add(new RowColumn(row, col));
+      }
+
+      long t1 = System.currentTimeMillis();
+
+      for (RowColumn rowColumn : rowcols) {
+        snap.get(rowColumn.getRow(), rowColumn.getColumn());
+      }
+
+      long t2 = System.currentTimeMillis();
+
+      snap.gets(rowcols);
+
+      long t3 = System.currentTimeMillis();
+
+      System.out.printf("test3 time 1:%d  time2:%d\n",(t2-t1),(t3-t2));
+    }
+  }
+```
+
+The program above outputs :
+
+```
+test1 time 1:294  time2:13
+test2 time 1:651  time2:25
+test3 time 1:153  time2:7
+```
+
+[get]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#gets-java.lang.CharSequence-org.apache.fluo.api.data.Column-
+[getsc]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#gets-java.lang.CharSequence-java.util.Set-
+[getmc]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#gets-java.util.Collection-java.util.Set-
+[getrc]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#gets-java.util.Collection-
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/observer_example.md
----------------------------------------------------------------------
diff --git a/tour/observer_example.md b/tour/observer_example.md
new file mode 100644
index 0000000..0042a70
--- /dev/null
+++ b/tour/observer_example.md
@@ -0,0 +1,80 @@
+---
+title: Observer Example
+---
+
+The following code shows how to setup and trigger an observer.  The observer is triggered when the
+column *obs:data* is changed.
+
+```java
+  public static final Column OBSERVED_COL = new Column("obs","data");
+  public static final Column INVERT_COL = new Column("inv","data");
+
+  public static class MyObserver extends AbstractObserver {
+
+    @Override
+    public void process(TransactionBase tx, Bytes row, Column col) throws Exception {
+      //invert column and value
+      Bytes value = tx.get(row, col);
+      tx.set(value, INVERT_COL, row);
+    }
+
+    @Override
+    public ObservedColumn getObservedColumn() {
+      return new ObservedColumn(OBSERVED_COL, NotificationType.STRONG);
+    }
+  }
+
+  private static void preInit(FluoConfiguration fluoConfig) {
+    //configure Fluo to use MyObserver before initialization
+    fluoConfig.addObserver(new ObserverSpecification(MyObserver.class.getName()));
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    try(Transaction tx1 = client.newTransaction()) {
+      tx1.set("kerbalnaut0001", OBSERVED_COL, "Jebediah");
+      tx1.commit();
+    }
+
+    try(Transaction tx2 = client.newTransaction()) {
+      tx2.set("kerbalnaut0002", OBSERVED_COL, "Bill");
+      tx2.commit();
+    }
+
+    mini.waitForObservers();
+
+    try(Snapshot snap = client.newSnapshot()) {
+      snap.scanner().build().forEach(System.out::println);
+    }
+  }
+```
+
+The code above prints :
+
+```
+Bill inv data  kerbalnaut0002
+Jebediah inv data  kerbalnaut0001
+kerbalnaut0001 obs data  Jebediah
+kerbalnaut0002 obs data  Bill
+```
+
+The following events happen when this code is run.
+
+ * *tx1* modifies *kerbalnaut0001:obs:data* causing *MyObserver* to run later on that row+column.
+ * *tx2* modifies *kerbalnaut0002:obs:data* causing *MyObserver* to run later on that row+column.
+ * Later *MyObserver* is run and passed row+column *kerbalnaut0001:obs:data*
+ * Later *MyObserver* is run and passed row+column *kerbalnaut0002:obs:data*
+
+Observers are run in the background by Fluo threads.  Fluo also creates the
+transaction passed to an Observer and commits it.  The transaction does not
+need to call commit and can not, the TransactionBase type passed to an Observer
+does not have a commit method.   The framework handles committing because it
+retries in case of a commit exception.
+
+Since observers are run in the background, you never know when they will run.
+For testing purposes MiniFluo provides the waitForObservers() method that is
+called above.  This method waits for all notifications to be processed by
+observers.
+
+There is no stand alone exercise for the Observer.  Hands on experience with it can be obtained by
+completing the [word count exercise](/tour/exercise-1/) which is the next step in the tour.
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/observers.md
----------------------------------------------------------------------
diff --git a/tour/observers.md b/tour/observers.md
new file mode 100644
index 0000000..d6ae2c2
--- /dev/null
+++ b/tour/observers.md
@@ -0,0 +1,27 @@
+---
+title: Observer Concepts
+---
+
+Fluo supports complex processing by running user provided Observers which are triggered by
+notifications.   An Observer request that the system run it when a certain column is modified.  When
+another transaction modifies an observed column, it will persist a notification that later causes
+the Observer to run.  When an Observers is run, its provided with the row and column that caused it
+to run along with a transaction.  Fluo worker processes running across a cluster will execute
+Observers.
+
+Since all transactions need to know which columns trigger observers, observer must be registered
+with Fluo at initialization time.
+
+Fluo supports two type of notifications :
+
+ * **Strong notifications:** guarantee an observer will run at most once when a column is modified.
+   If multiple transactions modify an observed row+column before an observer runs, it will only run
+   once.   It will not run once for each modification.
+ * **Weak notifications:** cause an observer to run at least once.  Observers may run multiple times
+   and/or concurrently based on a single weak notification.  In order to guarantee strong
+   notifications run an observer at most once, strong notifications are part of the transaction
+   model. Therefore a strong notification can cause transaction collisions.  Weak notifications are
+   not transactional and will not cause collisions. Therefore in situations where many transactions
+   are notifying a row+column concurrently, using weak notifications is best.
+
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/pg0-intro.md
----------------------------------------------------------------------
diff --git a/tour/pg0-intro.md b/tour/pg0-intro.md
deleted file mode 100644
index 4cbbe84..0000000
--- a/tour/pg0-intro.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Fluo Tour
----------
-
-This tour provides a sequence of pages that introduce Fluo concepts with
-associated coding excercies.  If you have any questions along the way, please
-ask questions in IRC or on the dev list TODO link.   

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/pg5-snapshot-isolation.md
----------------------------------------------------------------------
diff --git a/tour/pg5-snapshot-isolation.md b/tour/pg5-snapshot-isolation.md
deleted file mode 100644
index c5f6eba..0000000
--- a/tour/pg5-snapshot-isolation.md
+++ /dev/null
@@ -1,20 +0,0 @@
-Snapshot Isolation
-------------------
-
-Fluo provides Snapshot isolation.  This means that a Transaction or Snapshot
-can only see what was commited before it started.   The following steps
-demonstrate the concept of snapshot isolation. Try to code up the steps using
-Fluo, then run it and see if it prints what you expect.  If there is something
-you are unsure about, code for the following steps is on the next page.
-
-
- * Create transaction *tx1*
- * Using *tx1* set row=kerbalnaut0001 fam=name qual=last to Kerbin
- * Commit *tx1*
- * Create transaction *tx2*
- * Using *tx2* set row=kerbalnaut0001 fam=name qual=last to Kerman
- * Create snapshot *s1*
- * Commit *tx2*
- * Create snapshot *s2*
- * Using *s1* print row=kerbalnaut0001 fam=name qual=last
- * Using *s2* print row=kerbalnaut0001 fam=name qual=last

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/pg6-snapshot-isolation-code.md
----------------------------------------------------------------------
diff --git a/tour/pg6-snapshot-isolation-code.md b/tour/pg6-snapshot-isolation-code.md
deleted file mode 100644
index 55177fd..0000000
--- a/tour/pg6-snapshot-isolation-code.md
+++ /dev/null
@@ -1,40 +0,0 @@
-Snapshot Isolation Code
------------------------
-
-```java
-  private static void excercise(MiniFluo mini, FluoClient client) {
-    
-    String row = "kerbalnaut0001";
-    Column lName = new Column("name", "last");
-    
-    try(Transaction tx1 = client.newTransaction()) {
-      tx1.set(row, lName, "Kerbin");
-      tx1.commit();
-    }
-    
-    try(Transaction tx2 = client.newTransaction()) {
-      tx2.set(row, lName, "Kerman");
-      
-      Snapshot s1 = client.newSnapshot();
-      
-      tx2.commit();
-      
-      Snapshot s2 = client.newSnapshot();
-      
-      System.out.println(s1.gets(row, lName));
-      System.out.println(s2.gets(row, lName));
-      
-      s1.close();
-      s2.close();
-    }
-  }
-```
-
-The code above prints :
-
-```
-Kerbin
-Kerman
-```
-
-

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/recipes.md
----------------------------------------------------------------------
diff --git a/tour/recipes.md b/tour/recipes.md
new file mode 100644
index 0000000..7a02888
--- /dev/null
+++ b/tour/recipes.md
@@ -0,0 +1,6 @@
+---
+title: Fluo Recipes
+---
+
+Coming soon, a tour of [Fluo Recipes](https://github.com/apache/fluo-recipes).
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/row-locking.md
----------------------------------------------------------------------
diff --git a/tour/row-locking.md b/tour/row-locking.md
new file mode 100644
index 0000000..a105611
--- /dev/null
+++ b/tour/row-locking.md
@@ -0,0 +1,78 @@
+---
+title: Row Locking
+---
+
+Fluo relies on Accumulo's conditional mutations to implement cross node
+transactions.  Conditional mutations lock entire rows on the server side when
+checking conditions.  These row locks can impact the performance of your
+transactions, so its something to be aware of when designing a schema.
+
+The following code demonstrate the impact of schema design on performance. The
+code adds lots of edges to a single node in a graph using many transactions and
+threads. All of the edges are added to a single row.
+
+These performance problems may not occur on a single node with a single client,
+because Fluo clients batch a lot of operations related to committing.  To make
+the problem more apparent, the following code creates three clients and three
+loaders.
+
+```java
+  public static class EdgeLoader implements Loader {
+
+    private String node1;
+    private String node2;
+
+    public EdgeLoader(String node1, String node2) {
+      this.node1 = node1;
+      this.node2 = node2;
+    }
+
+    @Override
+    public void load(TransactionBase tx, Context ctx) throws Exception {
+      tx.set(node1, new Column("edge", node2), "");
+    }
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+
+    String node1 = "n00000";
+
+    long t1 = System.currentTimeMillis();
+
+    try (LoaderExecutor le1 = client.newLoaderExecutor();
+        FluoClient client2 = FluoFactory.newClient(mini.getClientConfiguration());
+        LoaderExecutor le2 = client2.newLoaderExecutor();
+        FluoClient client3 = FluoFactory.newClient(mini.getClientConfiguration());
+        LoaderExecutor le3 = client2.newLoaderExecutor()) {
+
+      int start = 1;
+
+      for(LoaderExecutor le : Arrays.asList(le1, le2, le3)) {
+        for (int i = 1; i < 10000; i++) {
+          String node2 = String.format("%05d", i+start);
+          le.execute(new EdgeLoader(node1, node2));
+        }
+
+        start+=10000;
+      }
+    }
+
+    long t2 = System.currentTimeMillis();
+
+    System.out.println(t2 - t1);
+  }
+```
+
+Try running the code above and note the time.  Then change the load function to
+the following and run the code again.  You should see a significant decrease in
+the time it takes.  The change below spreads the edges over many rows.
+
+```java
+    @Override
+    public void load(TransactionBase tx, Context ctx) throws Exception {
+      tx.set(node1+":"+node2, new Column("edge", ""), "");
+    }
+```
+
+
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/scanning-code.md
----------------------------------------------------------------------
diff --git a/tour/scanning-code.md b/tour/scanning-code.md
new file mode 100644
index 0000000..2c58053
--- /dev/null
+++ b/tour/scanning-code.md
@@ -0,0 +1,76 @@
+---
+title: Scanning Code
+---
+
+
+```java
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    Column fName = new Column("name", "first");
+    Column lName = new Column("name", "last");
+    Column bravery = new Column("attr","bravery");
+
+    try(Transaction tx1 = client.newTransaction()) {
+      tx1.set("kerbalnaut0001", fName, "Jebediah");
+      tx1.set("kerbalnaut0001", lName, "Kerman");
+      tx1.set("kerbalnaut0001", bravery, "5");
+
+      tx1.set("kerbalnaut0002", fName, "Bill");
+      tx1.set("kerbalnaut0002", lName, "Kerman");
+      tx1.set("kerbalnaut0002", bravery, "2");
+
+      tx1.set("kerbalnaut0003", fName, "Bob");
+      tx1.set("kerbalnaut0003", lName, "Kerman");
+      tx1.set("kerbalnaut0003", bravery, "1");
+
+      tx1.set("bravery5", new Column("id", "kerbalnaut0001"), "5");
+      tx1.set("bravery2", new Column("id", "kerbalnaut0002"), "2");
+      tx1.set("bravery1", new Column("id", "kerbalnaut0003"), "1");
+
+      tx1.commit();
+    }
+
+    try(Snapshot s1 = client.newSnapshot()) {
+       //scan over an entire row
+       CellScanner cellScanner = s1.scanner().over(Span.exact("kerbalnaut0002")).build();
+       System.out.println("Scan 1 :");
+       for (RowColumnValue rcv : cellScanner) {
+         System.out.println("\t"+rcv);
+       }
+
+       //scan over a row and column family
+       cellScanner = s1.scanner().over(Span.exact("kerbalnaut0002", new Column("name"))).build();
+       System.out.println("\nScan 2 :");
+       for (RowColumnValue rcv : cellScanner) {
+         System.out.println("\t"+rcv);
+       }
+
+       //scan over two columns
+       cellScanner = s1.scanner().over(Span.prefix("kerbalnaut")).fetch(fName, bravery).build();
+       System.out.println("\nScan 3 :");
+       //use Java lamda's to print instead of foreach loop
+       cellScanner.forEach(rcv -> System.out.println("\t"+rcv));
+    }
+  }
+```
+
+The code above prints :
+
+```
+Starting MiniFluo ... started.
+Scan 1 :
+	kerbalnaut0002 attr bravery  2
+	kerbalnaut0002 name first  Bill
+	kerbalnaut0002 name last  Kerman
+
+Scan 2 :
+	kerbalnaut0002 name first  Bill
+	kerbalnaut0002 name last  Kerman
+
+Scan 3 :
+	kerbalnaut0001 attr bravery  5
+	kerbalnaut0001 name first  Jebediah
+	kerbalnaut0002 attr bravery  2
+	kerbalnaut0002 name first  Bill
+	kerbalnaut0003 attr bravery  1
+	kerbalnaut0003 name first  Bob
+```

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/scanning.md
----------------------------------------------------------------------
diff --git a/tour/scanning.md b/tour/scanning.md
new file mode 100644
index 0000000..6cd39f6
--- /dev/null
+++ b/tour/scanning.md
@@ -0,0 +1,36 @@
+---
+title: Scanning
+---
+
+In some situations you may want to read a range of data instead of specific rows and columns.  For
+this case Fluo supports [creating scanners][scanner] over ranges.   These scanners implement Java Iterable and
+can be used with foreach loops.  Scanners also support reading a subset of columns within a range.
+
+To specify a range, Fluo has a simple POJO called [Span].  The name was chosen so it would not
+conflict with Accumulo's Range. [Span] has multiple static helper methods for creating common
+ranges, like a range over all rows with a given prefix.  Try the following exercise using scanners.
+
+ * **Create transaction** *tx1*
+ * **Using** *tx1* **set** *kerbalnaut0001:name:last* **to** *Kerman*
+ * **Using** *tx1* **set** *kerbalnaut0001:name:first* **to** *Jebediah*
+ * **Using** *tx1* **set** *kerbalnaut0001:attr:bravery* **to** *5*
+ * **Using** *tx1* **set** *kerbalnaut0002:name:last* **to** *Kerman*
+ * **Using** *tx1* **set** *kerbalnaut0002:name:first* **to** *Bill*
+ * **Using** *tx1* **set** *kerbalnaut0002:attr:bravery* **to** *2*
+ * **Using** *tx1* **set** *kerbalnaut0003:name:last* **to** *Kerman*
+ * **Using** *tx1* **set** *kerbalnaut0003:name:first* **to** *Bob*
+ * **Using** *tx1* **set** *kerbalnaut0003:attr:bravery* **to** *1*
+ * **Using** *tx1* **set** *bravery5:id:kerbalnaut0001* **to** *5*
+ * **Using** *tx1* **set** *bravery2:id:kerbalnaut0002* **to** *2*
+ * **Using** *tx1* **set** *bravery1:id:kerbalnaut0003* **to** *1*
+ * **Commit** *tx1*
+ * **Create snapshot** *s1*
+ * **Using** *s1* **scan and print row** *kerbalnaut0002*
+ * **Using** *s1* **scan and print row** *kerbalnaut0002* **and column family** *name*
+ * **Using** *s1* **scan rows with prefix** *kerbalnaut* **with columns** *name:first* **and** *attr:bravery*
+
+Scanners also read data using snapshot isolation.  To show this try modifying the exercise above to
+change data after *s1* is created but before the scans happen.
+
+[Span]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/data/Span.html
+[scanner]: /apidocs/fluo/{{ site.latest_fluo_release }}/org/apache/fluo/api/client/SnapshotBase.html#scanner--

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/snapshot-isolation-code.md
----------------------------------------------------------------------
diff --git a/tour/snapshot-isolation-code.md b/tour/snapshot-isolation-code.md
new file mode 100644
index 0000000..ebf017f
--- /dev/null
+++ b/tour/snapshot-isolation-code.md
@@ -0,0 +1,41 @@
+---
+title: Snapshot Isolation Code
+---
+
+```java
+  private static void excercise(MiniFluo mini, FluoClient client) {
+
+    String row = "kerbalnaut0001";
+    Column lName = new Column("name", "last");
+
+    try(Transaction tx1 = client.newTransaction()) {
+      tx1.set(row, lName, "Kerbin");
+      tx1.commit();
+    }
+
+    try(Transaction tx2 = client.newTransaction()) {
+      tx2.set(row, lName, "Kerman");
+
+      Snapshot s1 = client.newSnapshot();
+
+      tx2.commit();
+
+      Snapshot s2 = client.newSnapshot();
+
+      System.out.println(s1.gets(row, lName));
+      System.out.println(s2.gets(row, lName));
+
+      s1.close();
+      s2.close();
+    }
+  }
+```
+
+The code above prints :
+
+```
+Kerbin
+Kerman
+```
+
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/snapshot-isolation.md
----------------------------------------------------------------------
diff --git a/tour/snapshot-isolation.md b/tour/snapshot-isolation.md
new file mode 100644
index 0000000..df100aa
--- /dev/null
+++ b/tour/snapshot-isolation.md
@@ -0,0 +1,20 @@
+---
+title: Snapshot Isolation
+---
+
+Fluo provides Snapshot isolation.  This means that a Transaction or Snapshot can only see data
+committed before it started.   The following steps demonstrate the concept of snapshot isolation. Try
+to code up the steps using Fluo, then run it and see if it prints what you expect.  If there is
+something you are unsure about, code for the following steps is on the next page.
+
+
+ * **Create transaction** *tx1*
+ * **Using** *tx1* **set** *kerbalnaut0001:name:last* **to** *Kerbin*
+ * **Commit** *tx1*
+ * **Create transaction** *tx2*
+ * **Using** *tx2* **set** *kerbalnaut0001:name:last* **to** *Kerman*
+ * **Create snapshot** *s1*
+ * **Commit** *tx2*
+ * **Create snapshot** *s2*
+ * **Using** *s1* **print** *kerbalnaut0001:name:last*
+ * **Using** *s2* **print** *kerbalnaut0001:name:last*

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/tx-logging.md
----------------------------------------------------------------------
diff --git a/tour/tx-logging.md b/tour/tx-logging.md
new file mode 100644
index 0000000..cfecdb1
--- /dev/null
+++ b/tour/tx-logging.md
@@ -0,0 +1,44 @@
+---
+title: Transaction Logging
+---
+
+Fluo can produce detailed logs about transactions if configured to do so.
+This can be useful debugging.  Modify `./src/main/resources/log4j.properties`
+and remove `#` on the line `#log4j.logger.fluo.tx=TRACE`.  After doing this
+rerun the collision exercise on the previous page.
+
+With this configuration change, you should see output like the following.
+Notice the logging shows what was read, set, and the collision information.
+
+```
+Starting MiniFluo ... started.
+TRACE: txid: 3 begin() thread: 10
+TRACE: txid: 3 set(kerbalnaut0001, name last , Kerma)
+TRACE: txid: 3 commit() -> SUCCESSFUL commitTs: 4
+TRACE: txid: 3 close()
+TRACE: txid: 3 thread : 10 time: ... #ret: 0 #set: 1 #collisions: 0 waitTime: 0 committed: true class: N/A
+TRACE: txid: 5 begin() thread: 10
+TRACE: txid: 6 begin() thread: 10
+TRACE: txid: 5 get(kerbalnaut0001, name last ) -> Kerma
+TRACE: txid: 5 set(kerbalnaut0001, name last , Kerman)
+TRACE: txid: 6 get(kerbalnaut0001, name last ) -> Kerma
+TRACE: txid: 6 set(kerbalnaut0001, name last , KermaN)
+TRACE: txid: 5 commit() -> SUCCESSFUL commitTs: 7
+TRACE: txid: 6 commit() -> UNSUCCESSFUL commitTs: -1
+TRACE: txid: 6 collisions: {kerbalnaut0001=[name last ]}
+tx3 commit exception : org.apache.fluo.api.exceptions.CommitException
+TRACE: txid: 6 close()
+TRACE: txid: 6 thread : 10 time: ... #ret: 1 #set: 1 #collisions: 1 waitTime: 0 committed: false class: N/A
+TRACE: txid: 5 close()
+TRACE: txid: 5 thread : 10 time: ... #ret: 1 #set: 1 #collisions: 0 waitTime: 0 committed: true class: N/A
+TRACE: txid: 8 begin() thread: 10
+TRACE: txid: 8 get(kerbalnaut0001, name last ) -> Kerman
+Kerman
+TRACE: txid: 8 close()
+TRACE: txid: 8 thread : 10 time: ... #ret: 1 #set: 0 #collisions: 0 waitTime: 0 committed: false class: N/A
+```
+
+More information about configuring logging is available in [the logging
+documentation](/docs/fluo/{{ site.latest_fluo_release }}/applications/#debugging-applications).
+
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/weak-code.md
----------------------------------------------------------------------
diff --git a/tour/weak-code.md b/tour/weak-code.md
new file mode 100644
index 0000000..7b1c2b7
--- /dev/null
+++ b/tour/weak-code.md
@@ -0,0 +1,75 @@
+---
+title: Weak Notification Code
+---
+
+```java
+  static Column NC = new Column("ntyf", "sum");
+  static Column TOTAL_COL = new Column("sum", "total");
+  static Column UPDATE_COL = new Column("sum", "update");
+
+  public static class SummingObserver extends AbstractObserver {
+
+    @Override
+    public ObservedColumn getObservedColumn() {
+      return new ObservedColumn(NC, NotificationType.WEAK);
+    }
+
+    @Override
+    public void process(TransactionBase tx, Bytes brow, Column col) throws Exception {
+
+      String row = brow.toString();
+
+      int sum = Integer.parseInt(tx.gets(row, TOTAL_COL, "0"));
+
+      CellScanner scanner = tx.scanner().over(Span.prefix(row +"/")).build();
+      for (RowColumnValue rcv : scanner) {
+        sum += Integer.parseInt(rcv.getsValue());
+        tx.delete(rcv.getRow(), rcv.getColumn());
+      }
+
+      System.out.println("sum : " + sum);
+
+      tx.set(row, TOTAL_COL, ""+sum);
+    }
+  }
+
+  private static void preInit(FluoConfiguration fluoConfig) {
+    fluoConfig.addObserver(new ObserverSpecification(SummingObserver.class.getName()));
+  }
+
+  private static void excercise(MiniFluo mini, FluoClient client) {
+    try (LoaderExecutor le = client.newLoaderExecutor()) {
+      Random r = new Random(42);
+      for (int i = 0; i < 5000; i++) {
+        //The Loader interface only has one function and can therefore be written as a lambda below.
+        le.execute((tx, ctx) -> {
+          String row = "counter001/"+String.format("%07d", r.nextInt(10_000_000));
+          int curVal = Integer.parseInt(tx.gets(row, UPDATE_COL, "0"));
+          tx.set(row, UPDATE_COL, curVal+1+"");
+          tx.setWeakNotification("counter001", NC);
+        });
+      }
+    }
+
+    mini.waitForObservers();
+
+    try(Snapshot snap = client.newSnapshot()) {
+      System.out.println("final sum : "+snap.gets("counter001", TOTAL_COL));
+    }
+  }
+```
+
+The code above will print something like the following.  Every run will print something slightly
+different because it depends on when the observer runs.
+
+```
+$ mvn -q clean compile exec:java
+Starting MiniFluo ... started.
+sum : 526
+sum : 1465
+sum : 2414
+sum : 3260
+sum : 4736
+sum : 5000
+final sum : 5000
+```

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/weak-notifications.md
----------------------------------------------------------------------
diff --git a/tour/weak-notifications.md b/tour/weak-notifications.md
new file mode 100644
index 0000000..9476de1
--- /dev/null
+++ b/tour/weak-notifications.md
@@ -0,0 +1,26 @@
+---
+title: Weak Notification Exercise
+---
+
+This exercise will use weak notification to update a shared counter.   The way the example works,
+many threads will concurrently try to update the counter.  However collisions will be avoided.
+
+Create an observer that observes column *ntfy:sum* and make it do the following.
+
+ * For the row triggered append `/` and sum everything with that row prefix.  Also delete the rows
+   with that prefix.
+ * Print the sum.
+ * For the triggering row, update the column *sum:total* with the new sum.
+
+Create a loader that does the following and run it 5000 times :
+
+ * Generate a random number in the range [0,10^7-1].  This will be refered to as *\<rand\>*.
+ * Add one to *counter001/\<rand\>:sum:update*
+ * Weakly notify *counter001:nfty:sum*
+
+After the loader finishes, wait for observers and then print the value for *counter001:sum:total*.
+Since weak notifications are not transactional, all of the threads notifying the same row column
+should not collide.
+
+One experiment to try is to use a smaller random number range and enable collision logging.  For
+example try generating random numbers in the range of [0,99].  

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/write-skew-code.md
----------------------------------------------------------------------
diff --git a/tour/write-skew-code.md b/tour/write-skew-code.md
new file mode 100644
index 0000000..f3fae1e
--- /dev/null
+++ b/tour/write-skew-code.md
@@ -0,0 +1,44 @@
+---
+title: Write Skew Code
+---
+
+```java
+ private static void excercise(MiniFluo mini, FluoClient client) {
+
+    Column sumCol = new Column("data", "sum");
+
+    try(Transaction tx1 = client.newTransaction()) {
+      tx1.set("n0", sumCol, "0");
+      tx1.set("n01", sumCol, "1");
+      tx1.set("n02", sumCol, "2");
+      tx1.commit();
+    }
+
+    try(Transaction tx2 = client.newTransaction(); Transaction tx3 = client.newTransaction()) {
+
+      int n01Sum = Integer.parseInt(tx2.gets("n01", sumCol));
+      int n02Sum = Integer.parseInt(tx2.gets("n02", sumCol));
+
+      tx2.set("n0", sumCol, n01Sum + n02Sum +"");
+      tx3.set("n01", sumCol, "5");
+
+      tx2.commit();
+      tx3.commit();
+    }
+
+    try(Snapshot s1 = client.newSnapshot()) {
+      System.out.println("n0 sum : "+s1.gets("n0", sumCol));
+      System.out.println("n01 sum : "+s1.gets("n01", sumCol));
+      System.out.println("n02 sum : "+s1.gets("n02", sumCol));
+    }
+  }
+```
+
+The code above prints :
+
+```
+n0  sum : 3
+n01 sum : 5
+n02 sum : 2
+```
+

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/write-skew.md
----------------------------------------------------------------------
diff --git a/tour/write-skew.md b/tour/write-skew.md
new file mode 100644
index 0000000..b14d6fd
--- /dev/null
+++ b/tour/write-skew.md
@@ -0,0 +1,31 @@
+---
+title: Write Skew
+---
+
+The page on collisions showed that if two transactions overlap and write the same data then one will
+fail.  However, in the case where two transactions overlap and one reads data that another is writing
+then both can succeed.  This behavior is called write skew.
+
+The example below shows write skew.  In the example *n0* is a node in a tree with two children *n01*
+and *n02*.  In *tx2* the sum of *n0* is set to the sum of its children.  However *tx2* misses the
+concurrent update from *tx3*.  Both *tx2* and *tx3* will commit successfully since they write to
+different keys.
+
+ * **Create transaction** *tx1*
+ * **Using** *tx1* **set** *n0:data:sum* **to** *0*
+ * **Using** *tx1* **set** *n01:data:sum* **to** *1*
+ * **Using** *tx1* **set** *n02:data:sum* **to** *2*
+ * **Commit** *tx1*
+ * **Create transaction** *tx2*
+ * **Create transaction** *tx3*
+ * **Using** *tx2* **set** *n0:data:sum* **to the value of** *n01:data:sum* **plus** *n02:data:sum*
+ * **Using** *tx3* **set** *n01:data:sum* **to** *5*
+ * **Commit** *tx2*
+ * **Commit** *tx3*
+ * **Create snapshot and print** *n0:data:sum*, *n01:data:sum*, **and** *n02:data:sum*
+
+The changes made by *tx3* will not be seen by *tx2*. This behavior is OK if the update made by *tx3*
+triggers a later update of *n0:data:sum*. Later pages in the tour will show that Observers can work
+this way, so that eventually the changes made by *tx3* are incorporated.  The [Weak Notification
+Exercise](/tour/weak-notifications/) later in the tour shows an example of an Observer that works
+like this.

http://git-wip-us.apache.org/repos/asf/incubator-fluo-website/blob/e3415fdb/tour/writing-code.md
----------------------------------------------------------------------
diff --git a/tour/writing-code.md b/tour/writing-code.md
index 5a4691a..474301b 100644
--- a/tour/writing-code.md
+++ b/tour/writing-code.md
@@ -1,12 +1,24 @@
 ---
 title: Writing and Running Fluo code
-tour_num: 3
-permalink: /tour/3/
 ---
 
-Following the Fluo tour will require writing code that uses Fluo's API.  In
-order to enable you to get quickly started writing Fluo code we have provided a
-git repository with a basic skeleton. Before continuing with the tour, please
-read about the [Fluo Tour git repository][1] and set it up locally.
+Following the Fluo tour will require writing code that uses Fluo's API.  There is a git repository
+with a basic skeleton that will help you get started quickly.   The commands below shows how to
+obtain, edit, and run this basic skeleton.
 
-[1]:https://github.com/keith-turner/fluo-tour
+```bash
+#clone branch with starter code
+git clone -b fluo-tour http://git.apache.org/incubator-fluo-website.git fluo-tour
+cd fluo-tour
+
+#edit Main (all of the following excercises will require this)
+nano src/main/java/ft/Main.java
+
+#using Maven, run Main (all of the following excercises will require this)
+mvn -q clean compile exec:java
+```
+
+Because it is so closely related to the website, this starter code is located in a branch of Fluo's
+website.  The starter project has its [own readme][readme] also.
+
+[readme]: https://github.com/apache/incubator-fluo-website/tree/fluo-tour


Mime
View raw message