Created
March 3, 2017 16:09
-
-
Save adkron/24ee285599b8731e1c77300755095810 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Live Like a Hippy</title> | |
<meta charset="utf-8"> | |
<style> | |
@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz); | |
@import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic); | |
@import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic); | |
body { | |
font-family: 'Droid Serif'; | |
} | |
li { | |
font-size: 2em; | |
} | |
li li { | |
font-size: 1em; | |
} | |
footer { | |
background-attachment: fixed; | |
background-position: left bottom; | |
position: absolute; | |
bottom: 30px; | |
left: 5px; | |
height: 60px; | |
width: 100%; | |
text-align: left; | |
} | |
footer img { | |
height: 100%; | |
width: auto; | |
} | |
h1, h2, h3 { | |
font-family: 'Yanone Kaffeesatz'; | |
font-weight: normal; | |
background-color: rgba(35, 27, 89, 0.9); | |
position: relative; | |
z-index: 100; | |
margin-top: 0px; | |
margin-bottom: 0px; | |
padding-left: 5%; | |
padding-right: 5%; | |
color: rgba(105, 81, 255, 1); | |
} | |
.remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; } | |
.remark-notes-preview-area { display: none; } | |
.remark-notes-current-area { | |
font-size: 1.5em; | |
} | |
.title { | |
background-image: url('images/hippies.jpg'); | |
background-size: auto 100%; | |
} | |
.family { | |
background-image: url('images/family2.jpg'); | |
background-size: 100% auto; | |
} | |
.umbrella { | |
background-image: url('images/umbrella.jpg'); | |
background-size: auto 100%; | |
} | |
.meaning { | |
background-image: url('images/hippies2.jpg'); | |
background-size: 100% auto; | |
} | |
.using-db { | |
background-image: url('images/db.png'); | |
background-size: 60% auto; | |
} | |
.processes { | |
background-image: url('images/processes.jpg'); | |
background-size: 100% auto; | |
} | |
.data-processes { | |
background-image: url('images/data-process2.png'); | |
background-size: auto 100%; | |
} | |
.why { | |
background-image: url('images/y.jpg'); | |
background-size: 100% auto; | |
} | |
.uncle-bob { | |
background-image: url('images/uncle_bob.png'); | |
background-size: 100% auto; | |
} | |
.horder { | |
background-image: url('images/horde.jpg'); | |
background-size: auto 100%; | |
} | |
.hoarder { | |
background-image: url('images/unorganized-hoarding.jpg'); | |
background-size: auto 100%; | |
} | |
.organized { | |
background-image: url('images/organized-hoarding.jpg'); | |
background-size: 100% auto; | |
} | |
.agent { | |
background-image: url('images/agent.jpg'); | |
background-size: auto 100%; | |
} | |
.agent-diagram { | |
background-image: url('images/agent-process.png'); | |
background-size: auto 80%; | |
} | |
.crashes { | |
background-image: url('images/hiro.png'); | |
background-size: auto 70%; | |
} | |
.genserver { | |
background-image: url('images/genserver-process.png'); | |
background-size: auto 100%; | |
} | |
.fault-tolerant { | |
background-image: url('images/failure-tolerance.png'); | |
background-size: auto 85%; | |
} | |
.lets-talk { | |
background-image: url('images/passive.jpg'); | |
background-size: auto 70%; | |
} | |
.me { | |
background-image: url('images/tintin.jpeg'); | |
background-size: auto 60%; | |
background-position: center bottom; | |
} | |
</style> | |
</head> | |
<body> | |
<textarea id="source"> | |
layout: true | |
<footer> <img src="images/small-circle.png"> | |
</footer> | |
--- | |
class: center, top, title | |
# Live Like a Hippy | |
## Amos King | |
### Elixir Daze 2017 - St. Augustine, Florida | |
--- | |
class: me | |
# Who am I? | |
* Amos King | |
* @adkron | |
* Binary Noggin | |
* This Agile Life | |
--- | |
class: center, top, family | |
# Spawned Processes | |
??? | |
I'm almost like Johnny | |
I like to spawn processes | |
Paul says that all great talks start with a story | |
--- | |
class: umbrella | |
# Umbrella App | |
--- | |
class: meaning | |
# What does it mean to live like a hippy? | |
??? | |
* Festivals are like conferences | |
* Free of constraints | |
* Enjoying life | |
* Enjoy making things themselves | |
* Clothes | |
* Jewelry | |
* Macramé | |
* Fault Tolerant Software | |
* Tie-dye | |
--- | |
class: using-db | |
Using the DB | |
============ | |
??? | |
* Password Reset System | |
* Better than another dependency for me | |
* Slower than some dependencies | |
* Didn't want this | |
* Demo - using_db | |
--- | |
class: center, middle | |
# Josh, the database is *NOT* your friend. | |
--- | |
class: processes | |
??? | |
* Saša Jurić | |
* Where do I store data? | |
* Give background of idea | |
* Sensible - Submit CFP | |
--- | |
class: data-processes | |
??? | |
* Store Data in your process | |
--- | |
class: why | |
# Why? | |
??? | |
* Why not grab a key value store | |
--- | |
class: ddos | |
# Because DDoS | |
??? | |
* Doesn't mean Distributed Denial of Service | |
--- | |
# Because DDoS | |
* Dependencies | |
??? | |
* One less library | |
* less to learn | |
* less to maintain | |
--- | |
# Because DDoS | |
* Dependencies | |
* Deployment | |
??? | |
* Configuring communication channels | |
* need less experts | |
--- | |
# Because DDoS | |
* Dependencies | |
* Deployment | |
* Optimization | |
??? | |
* what is faster than data in an in memory database? | |
--- | |
# Because DDoS | |
* Dependencies | |
* Deployment | |
* Optimization | |
* Security | |
??? | |
* Attack footprint | |
* Shodan 6,550 insecure Redis instances | |
* Unikernel | |
* machine image | |
* like embedded | |
* You app is the OS | |
--- | |
class: uncle-bob | |
??? | |
* Uncle Bob - Put off persistence as long as possible | |
* Architecture - The Lost Years - 2011 | |
--- | |
class: horder | |
# Horde(r)? | |
--- | |
class: hoarder | |
# Err, Hoarder | |
--- | |
class: organized | |
# Classify Our Data | |
??? | |
1. Classify your junk | |
2. Put it in the right place | |
3. Protect your junk | |
--- | |
Permanent | |
========= | |
* Static | |
* Changing | |
??? | |
## Static | |
* Config files | |
* Store in code | |
## Changing | |
* Medical History | |
* Blog comments | |
* User Data - Email, or Name | |
--- | |
Temporary | |
========= | |
* Temporal | |
* Transient | |
* Ephemeral | |
??? | |
* Temporal - Relating to Time - current weather | |
* Transient - lasting only a short time - Cache | |
* Ephemeral - lasting for a very short time - Transport | |
--- | |
class: agent | |
# Agent | |
??? | |
* Great simple data store | |
* Stores our data structures | |
* No conversion to wire protocol | |
* Quick | |
--- | |
class: agent-diagram | |
# What It Looks Like | |
??? | |
* separate business logic | |
* failure is likely to happen there | |
--- | |
#Agent Code | |
```elixir | |
defmodule PasswordReset.Hoard do | |
def start_link do | |
* Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def add(token, user) do | |
Agent.cast(__MODULE__, &Map.put(&1, token, user)) | |
end | |
def get_user(token) do | |
Agent.get(__MODULE__, &Map.fetch(&1, token)) | |
end | |
def remove(token) do | |
Agent.cast(__MODULE__, &Map.delete(&1, token)) | |
end | |
end | |
``` | |
??? | |
* starting state | |
--- | |
#Agent Code | |
```elixir | |
defmodule PasswordReset.Hoard do | |
def start_link do | |
Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def add(token, user) do | |
* Agent.cast(__MODULE__, &Map.put(&1, token, user)) | |
end | |
def get_user(token) do | |
Agent.get(__MODULE__, &Map.fetch(&1, token)) | |
end | |
def remove(token) do | |
Agent.cast(__MODULE__, &Map.delete(&1, token)) | |
end | |
end | |
``` | |
??? | |
* Agent.update/2 | |
* put user and token in | |
--- | |
#Agent Code | |
```elixir | |
defmodule PasswordReset.Hoard do | |
def start_link do | |
Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def add(token, user) do | |
Agent.cast(__MODULE__, &Map.put(&1, token, user)) | |
end | |
def get_user(token) do | |
* Agent.get(__MODULE__, &Map.fetch(&1, token)) | |
end | |
def remove(token) do | |
Agent.cast(__MODULE__, &Map.delete(&1, token)) | |
end | |
end | |
``` | |
??? | |
* grab the user out | |
--- | |
#Agent Code | |
```elixir | |
defmodule PasswordReset.Hoard do | |
def start_link do | |
Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def add(token, user) do | |
Agent.cast(__MODULE__, &Map.put(&1, token, user)) | |
end | |
def get_user(token) do | |
Agent.get(__MODULE__, &Map.fetch(&1, token)) | |
end | |
def remove(token) do | |
* Agent.cast(__MODULE__, &Map.delete(&1, token)) | |
end | |
end | |
``` | |
??? | |
* delete the user so the token can't be used again | |
--- | |
class: crashes | |
# What about crashes? | |
??? | |
Keep the data you are storing focused | |
"Let It Fail" | |
"Keep state together that changes together" | |
--- | |
class: genserver | |
# GenServer | |
??? | |
* Call | |
* Cast to push data - set it and forget it | |
* Custom termination logic | |
--- | |
# Failover Code | |
~~~elixir | |
defmodule PasswordResetHoard.FailoverStorage do | |
def start_link do | |
* Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def dump(state) do | |
Agent.cast(__MODULE__, fn(_) -> state end) | |
end | |
def load do | |
Agent.get(__MODULE__, fn(state) -> state end) | |
end | |
end | |
~~~ | |
--- | |
# Failover Code | |
~~~elixir | |
defmodule PasswordResetHoard.FailoverStorage do | |
def start_link do | |
Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def dump(state) do | |
* Agent.cast(__MODULE__, fn(_) -> state end) | |
end | |
def load do | |
Agent.get(__MODULE__, fn(state) -> state end) | |
end | |
end | |
~~~ | |
--- | |
# Failover Code | |
~~~elixir | |
defmodule PasswordResetHoard.FailoverStorage do | |
def start_link do | |
Agent.start_link(fn -> %{} end, name: __MODULE__) | |
end | |
def dump(state) do | |
Agent.cast(__MODULE__, fn(_) -> state end) | |
end | |
def load do | |
* Agent.get(__MODULE__, fn(state) -> state end) | |
end | |
end | |
~~~ | |
--- | |
# GenServer Code | |
~~~elixir | |
defmodule PasswordReset.Hoard do | |
def init(:ok) do | |
* {:ok,FailoverStorage.load} | |
end | |
# add remove get_user | |
def terminate(_reason, state) do | |
FailoverStorage.dump(state) | |
end | |
end | |
~~~ | |
--- | |
# GenServer Code | |
~~~elixir | |
defmodule PasswordReset.Hoard do | |
def init(:ok) do | |
{:ok,FailoverStorage.load} | |
end | |
# add remove get_user | |
def terminate(_reason, state) do | |
* FailoverStorage.dump(state) | |
end | |
end | |
~~~ | |
--- | |
class: fault-tolerant | |
# Failure Tolerance | |
??? | |
* Most likely failure at the bottom | |
* Supervisors - More chances before catastrophic failure | |
* Now the only failure is the system coming down | |
* Supervisor | |
* Default Max restarts = 3 | |
* Max Seconds = 5 | |
* Demo - master | |
--- | |
Gotchas | |
======= | |
* Expiration | |
* Distributed | |
* Binary Data | |
??? | |
* better solution Signed Keys and user id in url | |
* Move business logic away from | |
cachex - elixir lib | |
Phoenix.Presence | |
Chris McCord ElixirConf EU 2016 - Phoenix 1.2 | |
--- | |
Binary Data | |
=========== | |
* GC is different | |
* 64 bytes is a magic number | |
* GC only ~90% of system memory | |
??? | |
* UUID is binary | |
* 36 bytes under limit | |
* less than or equal 64 bytes in process heap | |
* greater than 64 bytes stored in ProcBin - shared memory and Ref counted | |
* Store Domain Representation | |
* Well documented search online | |
--- | |
# What's Next | |
* ETS | |
* DETS | |
* Mnesia | |
* Write an application with no DB | |
??? | |
* used maps | |
* why ets and dets | |
* code failures | |
* same api | |
* write to disk for crashing applications | |
* Mnesia | |
* from elixir school | |
* Do I need to roll back transactions? | |
* Do I need an easy to use syntax for reading and writing data? | |
* Should I store data across multiple nodes, rather than one? | |
* Do I need a choice where to store information (RAM or disk)? | |
--- | |
# How To Live Like a Hoarder | |
* Agent good for temporal data | |
* GenServer is your friend | |
* Push logic to the bottom | |
* Stack fail over data at the top | |
* Keep data in its own process | |
* Always have a backup | |
??? | |
--- | |
class: resources | |
# Resources | |
Slides | |
https://github.com/BinaryNoggin/live_like_a_hippy | |
Architecture the Lost Years | |
https://www.youtube.com/watch?v=WpkDN78P884 | |
Zen of Erlang | |
http://ferd.ca/the-zen-of-erlang.html | |
McCord - Phoenix 1.2 and Beyond | |
https://www.youtube.com/watch?v=n338leKvqnA | |
Cachex | |
https://github.com/zackehh/cachex | |
--- | |
# Thank You | |
* Elixir Daze | |
* Oliver Ferrigni | |
* Adam Ritzel | |
* Craig Buchek | |
* STL Elixir | |
--- | |
class: lets-talk | |
# Come Talk to Me | |
</textarea> | |
<script src="https://gnab.github.io/remark/downloads/remark-latest.min.js"> | |
</script> | |
<script> | |
var slideshow = remark.create({ | |
highlightLanguage: 'elixir', | |
highlightLines: true | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment