Skip to content

Instantly share code, notes, and snippets.

@adkron
Created March 3, 2017 16:09
Show Gist options
  • Save adkron/24ee285599b8731e1c77300755095810 to your computer and use it in GitHub Desktop.
Save adkron/24ee285599b8731e1c77300755095810 to your computer and use it in GitHub Desktop.
<!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