and other new ideas
Andrey Sitnik
Evil Martians
CRDT
for client-server communication
I am working at
@sitnikcode
Autoprefixer
PostCSS
Browserslist
The creator of
@sitnikcode
Autoprefixer was started in 2013
@sitnikcode
Now Autoprefixer and PostCSS grew up
@sitnikcode
They don’t need new features
Image: CLM BBDO Paris
It is a time for а new adventure
Part 1. What is next?
GraphQL is cool
@sitnikcode
But it is the present, not the future
Create the idea
Create API
Solve production problems
Polish the code
Promote
@sitnikcode
What’s the next big idea?
RPC
SOAP
REST
GraphQL
?
@sitnikcode
And then I saw CRDT talk by Victor Grishenko
Swarm.js
What I believe in
RPC
SOAP
REST
GraphQL
CRDT
¯\_(ツ)_/¯
not 100% sure
@sitnikcode
A way fo revolution
@sitnikcode
America
Indian spices
Round Earth theory
America
@sitnikcode
React
React
Functional programming
Facebook notifications
@sitnikcode
Redux
React Hot Loader
Elm
Redux
@sitnikcode
Part 2. Non-standard problem
A very standard problem
Buy a milk
0
Buy a break
0
Buy a whiskey
1
New to-do
Buy a whiskey
1
Leave a comment
You were prepared for this
@sitnikcode
Vuex
const action = {
  type: 'addTask',
  name: 'Buy a sugar',
  likes: 0,
  comment: ''
}
store.dispatch(action)@sitnikcode
commit('addStarted'})
fetch('/tasks', opts)
  .then(() => {
    commit('addComplete'})
  })
  .catch(() => {
    commit('addFailed'})
  })AJAX and loaders
@sitnikcode
  methods: {
    add() {
      // loading
      this.$apollo.mutate(mutation)
        .then(() => …)
        .catch(() => …)
    }
  }GraphQL and loaders
@sitnikcode
You finished the project
@sitnikcode
Small detail from client
I forgot a small detail.
Multiple users should edit the same task at the same moment.
Should not be that hard to add?
@sitnikcode
Should we ask user to merge conflict?
@sitnikcode
Buy a whiskey
Buy a vodka
Another user change the same task.
Please choose the right revision:
We need automaic edit conflict resolution
@sitnikcode
Buy a whiskey
Buy a vodka
Another user change the same task.
Please choose the right revision:
People shouldn’t suffer. Machines should suffer
@sitnikcode
We can add hacks around GraphQL …
@sitnikcode
… or you can go to the dungeons of CS
@sitnikcode
Distributed systems, 1982
@sitnikcode
Part 3. Distributed systems
Requests
GraphQL/AJAX request
The things
we care about
Don’t care
@sitnikcode
Distributed system
Sync
@sitnikcode
Conflicts resolution
{
  name: 'Водка',
  likes: 50
}{
  name: 'Виски',
  likes: 51
}?
@sitnikcode
“Bad programmers worry about the code.
            
            
Good programmers worry about
            
            
data structures”
        
        
— Linus Torvalds
Source: https://lwn.net/Articles/193245/
Sending the whole state is a problem
@sitnikcode
Event sourcing
set likes=50set name='Виски'set likes=50
set name='Виски'@sitnikcode
Event = Redux action
set likes=50{ type: 'SET_LIKES', value: 50 }@sitnikcode
Important part
No AJAX requests
showLoader()
fetch('/profile', {
  credentials: 'include',
  method: 'POST',
  form
}).then(r => {
  hideLoadaer()
}).catch(e => {
  …
})@sitnikcode
No mutations or sagas
const ADD_TODO = gql`
  mutation addTodo($type: String!) {
    addTodo(type: $type) {
      id
      type
    }
  }
`;
this.$apollo.mutate({
  …
})const createTask = write.bind(
  null, taskList, taskList.push,
  createTaskFailed)
function* watchCreateTask() {
  while (true) {
    let { payload } = yield take(CREATE_TASK);
    yield fork(createTask, payload.task);
  }
}@sitnikcode
No loaders
Continue to work
Save
@sitnikcode
Keep WebSockets
WS
@sitnikcode
Send Redux actions to server in background
Buy a break
Buy a break
dispatch({ type: 'DONE', taskId: 12 }){ type: 'DONE', taskId: 12 }WS
@sitnikcode
Optimistic UI, 2010
Continue to work
Save
Continue to work
Save
First mention: Google Wave Operational Transformation
@sitnikcode
Server re-send actions to other clients
{ type: 'DONE', taskId: 12 }WS
WS
@sitnikcode
Server could create Redux actions too
{ type: 'TRIAL_END' }WS
@sitnikcode
What if you need a loader?
Pessimistic UI
@sitnikcode
Use two actions for Pessimistic UI
{ type: 'PAY', … }{ type: 'PAID' }Offline?
@sitnikcode
Show global warning
Buy a milk
0
Buy a break
0
Buy a whiskey
1
Offline
@sitnikcode
Vuex is ready to keep actions in memory
@sitnikcode
DB error?
@sitnikcode
Vuex already has action undo in DevTools
@sitnikcode
Server can ask a client to undo wrong action
Name A
Rename to B
Name B
Rename to B
Undo
Name A
Part 4. The time
Distributed systems are not so easy
Step 1 Create an idea
Step2 Implement it
Every client could create action
Action
Client 1
Client 2
Action
Action
Action
@sitnikcode
Actions order problem
Rename to A
Final: B
Rename to B
Rename to B
Final: A
Rename to A
@sitnikcode
Time to maintain the same action order?
dispatch({ …, time: Date.now() })
actions.sortBy(i => i.time)@sitnikcode
Time: not unique
Date.now() //=> 153247098000
Date.now() //=> 153247098000@sitnikcode
Time: no consistency growth
Date.now() //=> 153247000011
// sudo ntpdate -s time.nist.gov
Date.now() //=> 153247000005@sitnikcode
Time: no synchroniziation across clients
13:15:00 UTC
12:10:00 UTC
1970-01-01 01:15:00
@sitnikcode
Return to distributed systems dungeons
@sitnikcode
No time = no problem
@sitnikcode
Logical clock, 1978
let counter = 0
function now () {
  counter += 1
  return counter + ':' + uniqueClientId
}Update counter after receiving actions
1:clientA
2:clientB
3:clientA
3:clientB
@sitnikcode
Part 5. Edit conflicts
Easy to merge changes in different fields
{
  type: 'setLikes',
  taskId: 1,
  likes: 50
}{
  type: 'changeName',
  taskId: 1,
  name: 'Buy a bread'
}@sitnikcode
But how to merge changes of the same field?
{
  type: 'setLikes',
  taskId: 1,
  likes: 50
}{
  type: 'setLikes',
  taskId: 1,
  likes: 50
}?
@sitnikcode
Collaborative text editing?
@sitnikcode
How to make actions atomic?
{
  ? // What put here
}@sitnikcode
Return to distributed systems dungeons
@sitnikcode
CRDT, 2009
@sitnikcode
One of them after my talk in Porto
Conflict-free replicated data types
…
@sitnikcode
Types are different
Op-based
State-based
With distributed time
Without timer
@sitnikcode
Every type has
@sitnikcode
P2P: every node could create event
Name = A, time: 2
Name = B, time: 1
@sitnikcode
No master
Same final state after synchronization
Name = A
Name = A
Name = A
Name = A
Name = A
Name = A
Name = A
Sorry, last write wins
@sitnikcode
Op-based counter
{
  type: 'setLikes',
  taskId: 1,
  likes: 51
}{
  type: 'addLike',
  taskId: 1
},
{
  type: 'removeLike',
  taskId: 1
}@sitnikcode
Action for changing every symbol in text?
c a t 1 2 3
delete symbol at index 1
cat → at
add symbol h after index 1
cat → chat
String:
Indexes:
@sitnikcode
Operations change symbol indexes
c a t 1 2 3
delete symbol at index 1
String:
Indexes:
a t 1 2
String:
Indexes:
@sitnikcode
Conflicts after merging
delete 1
add h after 1
cat → aht
delete symbol at index 1
cat → at
add symbol h after index 1
cat → chat
c a t 1 2 3
String:
Indexes:
@sitnikcode
Persistent indexes
c a t 1 2 3
delete symbol at index 1
String:
Indexes:
a t 2 3
String:
Indexes:
add symbol h after index 1
c a t 1 2 3
String:
Indexes:
c h a t 1 1.5 2 3
String:
Indexes:
@sitnikcode
Better merging
delete 1
add h to 1.5
cat → hat
delete symbol at index 1
cat → at
add symbol h to index 1.5
cat → chat
c a t 1 2 3
String:
Indexes:
@sitnikcode
CRDT is not a silver bullet
CRDT
Real apps
@sitnikcode
CRDT is inspiration for your custom types
Part 6. The result
Overview
@sitnikcode
We made a collaborative ToDo app
Buy a milk
0
Buy a break
0
Buy a whiskey
1
New to-do
Buy a whiskey
1
Leave a comment
But maybe we made something bigger?
New non-standard problem
Bring back old forgotten theory
Find it fitting for other things
@sitnikcode
Click to turn off
Click to turn on
Final state on
Save on to DB
Save off to DB
Use case: AJAX, GraphQL
Wrong order because of the network
@sitnikcode
Save on to DB
Ignore
Turn off, time: 1
Final state on
Use case: CRDT
Turn on, time: 2
@sitnikcode
AJAX & GraphQL were designed for
Image: Nikita Prokopov
@sitnikcode
CRDT was designed for
Image: Nikita Prokopov
@sitnikcode
40 % packet lost for Western websites
@sitnikcode
No stable Internet in NY subway
Image: Mr. Robot
Next billion users will not have good Internet
Image: John Stanmeyer
Feature 1
@sitnikcode
Bad connection support because CRDT is P2P
@sitnikcode
Option: Multi-master servers
@sitnikcode
Option: Cross-tab sync
Only leader tab keeps WS
@sitnikcode
Option: Mesh
@sitnikcode
Feature 2
@sitnikcode
Optimistic UI
Optimistic UI
Appling changes before server response
Offline support
Predictable conflict resolution
CRDT
@sitnikcode
Optimistic UI and live updates by design
@sitnikcode
Feature 3
@sitnikcode
“Every 100ms of additional latency costs Amazon 1% in profit”
More cases: pwastats.com
Performance → money
@sitnikcode
Feature 4
@sitnikcode
Atom Teletype
Visual Studio Live Share
CRDT in Redis
CRDT is a trend
SoundCloud
@sitnikcode
CRDT for regular apps
What I believe in
RPC
SOAP
REST
GraphQL
CRDT
¯\_(ツ)_/¯
not 100% sure
@sitnikcode
Many solutions with some of ideas
@sitnikcode
Logux: My CRDT framework
@sitnikcode
VPN ¯\_(ツ)_/¯
Thanks
VPN ¯\_(ツ)_/¯
VPN ¯\_(ツ)_/¯