Skip to content

rfc for now() in INSERT INTO #54

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions Options_for_a_now_function/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Options For A Now() Function

## Introduction

This document outlines the options for a `now()` function in TS SQL

## Purpose

To outline the various technical options and their uses cases, pros and cons and enable the correct product decision to be made

## Scope

The scope of this document is a `now()` function in an `INSERT` statement.

```sql
INSERT INTO mytable VALUES("myfamily", "myseries", now(), "mypayload");
```

The use of `now()` in a `SELECT` statement is out of scope (and assumed to be fine and jim dandy).

# Statement Of The Problem

A **naïve** implementation of `now()` in an `INSERT` statement would result in inconsistent data with a very high frequency.

For the purposes of exposition we shall assume a 3 node Riak cluster with a client writing to it and a remote `riak-shell`

The three Riak nodes suffer from a 1 hour clock drift.

The client writes to the cluster with a load balancer.

Consider this execution path:

![Problem Scenario](./problem_scenario.png)

We see that the results return:
```
{b, 13:00:00}
{a, 12:00:01}
{c, 11:00:02}
```

These are out of order.
Copy link

@matthewvon matthewvon Feb 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the problem statement is lacking. There were two problems noted in the email chain. First was arrival order as presented here. The second was key duplication which resulted in data being overwritten. Given that TS is focused upon queries with aggregation functions, the first problem might not be critical. Therefore the second problem could be the focus and performance issues of serialization could be avoided.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(a subtopic of key duplication was clock granularity. there have been conversations about using Raspberry Pi's for Riak TS instances. I suggest we document the clock granularity on a Pi too.)


This problem never occurs with `riak-shell` because it always connects to the same riak node:

![Problem Scenario](./riak_shell_basic.png)

## The Correct Way

The correct way is to implement a consistent server-side timestamp path. That timestamp could be used with a SQL command like:

```sql
INSERT INTO mytable VALUES("a", now());
```

or in the `CREATE TABLE` command like:

```sql
CREATE TABLE GeoCheckin
(
id SINT64 NOT NULL,
region VARCHAR NOT NULL,
state VARCHAR NOT NULL,
time TIMESTAMP NOT NULL AUTO,
weather VARCHAR NOT NULL,
temperature DOUBLE,
PRIMARY KEY (
(id, QUANTUM(time, 15, 'm')),
id, time
)
);
```

The execution flow is the same but different routing questions are asked - let us use the `INSERT INTO` statement for exposition purposes. We will only show the `client` path - the `riak-shell` one is trivially identical:

![Correct Way](./correct_way.png)

There are two variants on the ***correct way***:

* **strictly correct** if the canonical node isn't available the write fails
* **bit shit mode** if the canonical node isn't available go to fall back and let the guarantees go to hell in the proverbial

**Note**: oh, yeah, if you go ***bit shit*** yon John Daily runs you down in a banana-yella jeep, so there is that...

## Convenience Option

In this world `now()` in insert is seen as a convenience for play about and is optimised for onboarding and not production.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the email chain was a comment stating that Erlang's now() guaranteed a unique return value (within that VM). Please note this quote: "erlang:now/0 is deprecated, as it is and will be a scalability bottleneck." That is from http://erlang.org/doc/apps/erts/time_correction.html


In this world we introduce a lexical token in the `riak-shell` that forces time serialisation in the `riak-client` only:

![Convenience Option](./convenience_option.png)

## Choices

I go for convenience option - targetted at onboarding - and if we are going to go down the correct route we should design and implement the `CREATE TABLE` version first and then add `now()` to the `INSERT INTO` path.
61 changes: 61 additions & 0 deletions Options_for_a_now_function/convenience_option.msc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// diagram for upgrade_downgrade_specs.md
//
// Upgrade/Downgrade Scenario 1 diagram
//
// This diagram is generated by mscgen
// which can be installed on ubuntu by
// sudo apt-get install mscgen
//
// Generate this diagram on the command line with:
// > mscgen -T png -i problem_scenario.msc -F ./fonts/DejaVuSansMono.ttf
//
msc {

// Image defaults
width="1200", wordwraparcs="1";

// declare objects
// This is a cluster with 3 nodes for exposition purposes
// a client and a riak-shell

"Client", "Node 1", "Node 2", "Node 3", "Riak Shell";

"Node 1" box "Node 1" [label="\nTime: T + 1h\n"];
"Node 2" box "Node 2" [label="\nTime: T\n"];
"Node 3" box "Node 3" [label="\nTime: T - 1h\n"];

...;

|||;

---;

"Riak Shell" box "Riak Shell" [label="\nINSERT INTO mytable VALUES(a, $now);\n"],

|||;

"Riak Shell" => "Riak Shell" [label = "\nexpand $now =>'2016-04-23 11:12:13'\n"];

|||;

"Riak Shell" => "Node 2" [label = "send INSERT INTO\n\nmytable VALUES(a, '2016-04-23 11:12:13');"];

|||;

"Node 2" => "Node 2" [label = "write {a, '2016-04-23 11:12:13'}"];

|||;

...;

|||;

"Client" => "Node 1" [label = "send INSERT INTO\n\nmytable VALUES(a, $now);"];

|||;

"Node 1" => "Client" [label = "Error: syntax error"];

|||;

}
Binary file added Options_for_a_now_function/convenience_option.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions Options_for_a_now_function/correct_way.msc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// diagram for upgrade_downgrade_specs.md
//
// Upgrade/Downgrade Scenario 1 diagram
//
// This diagram is generated by mscgen
// which can be installed on ubuntu by
// sudo apt-get install mscgen
//
// Generate this diagram on the command line with:
// > mscgen -T png -i problem_scenario.msc -F ./fonts/DejaVuSansMono.ttf
//
msc {

// Image defaults
width="1200", wordwraparcs="1";

// declare objects
// This is a cluster with 3 nodes for exposition purposes
// a client and a riak-shell

"Client", "Node 1", "Node 2", "Node 3", "Riak Shell";

"Node 1" box "Node 1" [label="\nTime: T + 1h\n"];
"Node 2" box "Node 2" [label="\nTime: T\n"];
"Node 3" box "Node 3" [label="\nTime: T - 1h\n"];

...;

|||;

---;

"Client" => "Node 1" [label = "send INSERT INTO\n\nmytable VALUES(a, now())"];

|||;

"Node 1" box "Node 1" [label="\nidentify canonical vnode\n"],

|||;

"Node 1" => "Node 2" [label = "send INSERT INTO\n\nmytable VALUES(a, now())"];

|||;

"Node 2" box "Node 2" [label="\n evaluate now() => 12:00:00\n"],

|||;

"Node 2" => "Node 2" [label = "write {a, 12:00:00}"];

|||;

"Client" => "Node 2" [label = "send INSERT INTO\n\nmytable VALUES(b, now())"];

|||;

"Node 2" box "Node 2" [label="\nidentify canonical vnode\n"],

|||;

"Node 2" box "Node 2" [label="\n evaluate now() => 12:00:01\n"],

|||;

"Node 2" => "Node 2" [label = "write {b, 12:00:01}"];

|||;

}
Binary file added Options_for_a_now_function/correct_way.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions Options_for_a_now_function/problem_scenario.msc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// diagram for upgrade_downgrade_specs.md
//
// Upgrade/Downgrade Scenario 1 diagram
//
// This diagram is generated by mscgen
// which can be installed on ubuntu by
// sudo apt-get install mscgen
//
// Generate this diagram on the command line with:
// > mscgen -T png -i problem_scenario.msc -F ./fonts/DejaVuSansMono.ttf
//
msc {

// Image defaults
width="1200", wordwraparcs="1";

// declare objects
// This is a cluster with 3 nodes for exposition purposes
// a client and a riak-shell

"Client", "Node 1", "Node 2", "Node 3", "Riak Shell";

"Node 1" box "Node 1" [label="\nTime: T + 1h\n"];
"Node 2" box "Node 2" [label="\nTime: T\n"];
"Node 3" box "Node 3" [label="\nTime: T - 1h\n"];

...;

|||;

---;

"Client" => "Node 1" [label = "send INSERT INTO\n\nmytable VALUES(a, now())"];

|||;

"Node 1" box "Node 1" [label="\n evaluate now() => 13:00:00\n"],

|||;

"Node 1" => "Node 3" [label = "write {a, 13:00:00}"];

|||;

"Client" => "Node 2" [label = "send INSERT INTO mytable\n\nVALUES(b, now())"];

|||;

"Node 2" box "Node 2" [label="\n evaluate now() => 12:00:01\n"],

|||;

"Node 2" => "Node 3" [label = "write {b, 12:00:01}"];

|||;

"Client" => "Node 3" [label = "send INSERT INTO mytable\n\nVALUES(c, now())"];

|||;

"Node 3" box "Node 3" [label="\n evaluate now() => 11:00:02\n"],

|||;

"Node 3" => "Node 3" [label = "write {c, 11:00:02}"];

|||;

}
Binary file added Options_for_a_now_function/problem_scenario.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions Options_for_a_now_function/riak_shell_basic.msc
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// diagram for upgrade_downgrade_specs.md
//
// Upgrade/Downgrade Scenario 1 diagram
//
// This diagram is generated by mscgen
// which can be installed on ubuntu by
// sudo apt-get install mscgen
//
// Generate this diagram on the command line with:
// > mscgen -T png -i problem_scenario.msc -F ./fonts/DejaVuSansMono.ttf
//
msc {

// Image defaults
width="1200", wordwraparcs="1";

// declare objects
// This is a cluster with 3 nodes for exposition purposes
// a client and a riak-shell

"Client", "Node 1", "Node 2", "Node 3", "Riak Shell";

"Node 1" box "Node 1" [label="\nTime: T + 1s\n"];
"Node 2" box "Node 2" [label="\nTime: T\n"];
"Node 3" box "Node 3" [label="\nTime: T - 1s\n"];

...;

|||;

---;

"Riak Shell" => "Node 2" [label = "send INSERT INTO\n\nmytable VALUES(a, now())"];

|||;

"Node 2" box "Node 2" [label="\n evaluate now() => 12:00:00\n"],

|||;

"Node 2" => "Node 3" [label = "write {a, 12:00:00}"];

|||;

...;

|||;

"Riak Shell" => "Node 2" [label = "send INSERT INTO\n\nmytable VALUES(b, now())"];

|||;

"Node 2" box "Node 2" [label="\n evaluate now() => 12:00:01\n"],

|||;

"Node 2" => "Node 3" [label = "write {b, 12:00:01}"];

|||;


}
Binary file added Options_for_a_now_function/riak_shell_basic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.