Rudi Engelbrecht explores Maglev in a hands on tutorial. Topics such as transparent object persistence, concurrency, multiple Ruby VM's accessing the same objects, persisting Ruby procs and Continuations are illustrated. Source code is on https://github.com/rle
3. MagLev History
• Smalltalk Virtual Machine
Rubyfuza - Cape Town - 2012
4. MagLev History
• Smalltalk Virtual Machine
• Mature, used in production, large sites
Rubyfuza - Cape Town - 2012
5. MagLev History
• Smalltalk Virtual Machine
• Mature, used in production, large sites
• GemStone
Rubyfuza - Cape Town - 2012
6. MagLev History
• Smalltalk Virtual Machine
• Mature, used in production, large sites
• GemStone
• Transactional Memory Object Server for Smalltalk
Rubyfuza - Cape Town - 2012
7. MagLev History
• Smalltalk Virtual Machine
• Mature, used in production, large sites
• GemStone
• Transactional Memory Object Server for Smalltalk
• Maglev
Rubyfuza - Cape Town - 2012
8. MagLev History
• Smalltalk Virtual Machine
• Mature, used in production, large sites
• GemStone
• Transactional Memory Object Server for Smalltalk
• Maglev
• Transactional Memory Object Server for Ruby
Rubyfuza - Cape Town - 2012
19. MagLev Concepts
• Transparent Object Persistence
• Large Object Space
Rubyfuza - Cape Town - 2012
20. MagLev Concepts
• Transparent Object Persistence
• Large Object Space
• limited by disk space (not RAM)
Rubyfuza - Cape Town - 2012
21. MagLev Concepts
• Transparent Object Persistence
• Large Object Space
• limited by disk space (not RAM)
• Multi-user
Rubyfuza - Cape Town - 2012
22. MagLev Concepts
• Transparent Object Persistence
• Large Object Space
• limited by disk space (not RAM)
• Multi-user
• Thousands of virtual machines
Rubyfuza - Cape Town - 2012
23. MagLev Concepts
• Transparent Object Persistence
• Large Object Space
• limited by disk space (not RAM)
• Multi-user
• Thousands of virtual machines
• Sharing one object space
Rubyfuza - Cape Town - 2012
24. MagLev Concepts
• Transparent Object Persistence
• Large Object Space
• limited by disk space (not RAM)
• Multi-user
• Thousands of virtual machines
• Sharing one object space
• Isolation of each user’s view (repeatable read)
Rubyfuza - Cape Town - 2012
27. MagLev Concepts
• Concurrency
• Multi Version Concurrency Control
Rubyfuza - Cape Town - 2012
28. MagLev Concepts
• Concurrency
• Multi Version Concurrency Control
• Optimistic and Pessimistic models
Rubyfuza - Cape Town - 2012
29. MagLev Concepts
• Concurrency
• Multi Version Concurrency Control
• Optimistic and Pessimistic models
• Stores any Ruby Object
Rubyfuza - Cape Town - 2012
30. MagLev Concepts
• Concurrency
• Multi Version Concurrency Control
• Optimistic and Pessimistic models
• Stores any Ruby Object
• including it’s code (methods)
Rubyfuza - Cape Town - 2012
31. Ruby
VM
Ruby
VM Shared
Page Repository
Cache
Ruby
VM
Ruby
VM
34. MagLev - Magic Hat
• Rabbit in Hat Trick
Rubyfuza - Cape Town - 2012
35. class Person
attr_accessor :username, :followers
def initialize(username)
@username = username
@followers = Array.new
end
def add_follower(person)
@followers << person
end
def remove_follower(person)
@followers.delete(person)
end
def to_s
result = "@#{@username} --> [#{@followers.size}]"
@followers.each {|f| result = result + " #{f.username}" }
result
end
end
Rubyfuza - Cape Town - 2012
36. class Person
attr_accessor :username, :followers
def initialize(username)
@username = username
@followers = Array.new
end
...
end
Rubyfuza - Cape Town - 2012
37. ...
def add_follower(person)
@followers << person
end
def remove_follower(person)
@followers.delete(person)
end
...
Rubyfuza - Cape Town - 2012
38. ...
def to_s
result = "@#{@username} --> [#{@followers.size}]"
@followers.each {|f| result = result + "
#{f.username}" }
result
end
...
Rubyfuza - Cape Town - 2012
39. Maglev.persistent do
require 'person'
end
Maglev::PERSISTENT_ROOT[:persons] = Array.new
Maglev.commit_transaction
Rubyfuza - Cape Town - 2012
67. class Person
attr_accessor :username, :email, :followers,
:following, :tweets
def initialize(username)
@username = username
@email = username + “@email.com”
@followers = Array.new
@following = Array.new
@tweets = Array.new
end
...
end
Rubyfuza - Cape Town - 2012
68. ...
def follows(person)
@following << person
person.add_follower(self)
end
def unfollows(person)
@following.delete(person)
person.remove_follower(self)
end
...
Rubyfuza - Cape Town - 2012
69. ...
def add_follower(person)
@followers << person
end
def remove_follower(person)
@followers.delete(person)
end
...
Rubyfuza - Cape Town - 2012
70. ...
def tweets(text)
@tweets << Tweet.new(self, text)
end
...
Rubyfuza - Cape Town - 2012
71. ...
def to_s
result = "@#{@username} tweeted #{@tweets.size} times, follows
#{@following.size} persons and is followed #{@followers.size} times"
result = result + " , following: {"
@following.each {|f| result = result + " #{f.username}"}
result = result + " }"
result = result + " followed by: {"
@followers.each {|f| result = result + " #{f.username}"}
result = result + " }"
result
end
...
Rubyfuza - Cape Town - 2012
72. Maglev.persistent do
require 'person'
require 'tweet'
end
Maglev::PERSISTENT_ROOT[:persons] = Array.new
Maglev.commit_transaction
Rubyfuza - Cape Town - 2012
75. dhh obie
followers
dhh followers
obie
following following
unclebob
followers
dhh
Concurrent updates (in different VM’s) following
to same object fails
76. dhh obie
followers
dhh followers
obie
following following
unclebob
followers
dhh
Concurrent updates (in different VM’s) following
to same object fails
77. :persons
Array
dhh obie
followers
dhh followers
obie
dhh obie
following following
unclebob
noob1 noob2 unclebob
followers
dhh
Concurrent updates (in different VM’s) following
to same object fails
78. :persons
Array
dhh obie
followers
dhh followers
obie
dhh obie
following following
unclebob
noob1 noob2 unclebob
followers
dhh
Concurrent updates (in different VM’s) following
to same object fails
79. :persons
Array
dhh obie
followers
dhh followers
obie
dhh obie
following following
unclebob
noob1 noob2 unclebob
followers
dhh
Concurrent updates (in different VM’s) following
to same object fails
80. :persons
Array
dhh obie
followers
dhh followers
obie
dhh obie
following following
unclebob
noob1 noob2 unclebob
followers
dhh
Concurrent updates (in different VM’s) following
to same object fails
82. MagLev - Locks
• Read lock on object
Rubyfuza - Cape Town - 2012
83. MagLev - Locks
• Read lock on object
• Use object’s value and commit without fear that some other
transaction has committed a new value for that object during
your transaction
Rubyfuza - Cape Town - 2012
84. MagLev - Locks
• Read lock on object
• Use object’s value and commit without fear that some other
transaction has committed a new value for that object during
your transaction
• Guarantees that other sessions cannot:
a) acquire a write lock on the object
b) commit if they have written the object
Rubyfuza - Cape Town - 2012
86. MagLev - Locks
• Write lock on object
Rubyfuza - Cape Town - 2012
87. MagLev - Locks
• Write lock on object
• Guarantees that you can write the object and commit.
Rubyfuza - Cape Town - 2012
88. MagLev - Locks
• Write lock on object
• Guarantees that you can write the object and commit.
• Guarantees that other sessions cannot:
a) acquire a read or write lock on the object
b) commit if they have written the object
Rubyfuza - Cape Town - 2012
89. MagLev - Locks
• Write lock on object
• Guarantees that you can write the object and commit.
• Guarantees that other sessions cannot:
a) acquire a read or write lock on the object
b) commit if they have written the object
• Only one session can hold a write lock on an object - no
other session can hold any kind of lock on the object.
Rubyfuza - Cape Town - 2012
90. MagLev - Procs
y = 4
proc = Proc.new {|x| x * y}
proc.call(4)
Rubyfuza - Cape Town - 2012
91. MagLev - Continuation
Thread.start { callcc {|cc| $cont = cc}; p “Run after
One pretty callcc” }
Maglev::PERSISTENT_ROOT[:cc] = $cont
Maglev.commit_transaction
Rubyfuza - Cape Town - 2012
94. MagLev - Summary
• Rabbit in hat trick
Rubyfuza - Cape Town - 2012
95. MagLev - Summary
• Rabbit in hat trick
• Concurrent access to objects in distributed VM’s
Rubyfuza - Cape Town - 2012
96. MagLev - Summary
• Rabbit in hat trick
• Concurrent access to objects in distributed VM’s
• Proc’s can be persisted
Rubyfuza - Cape Town - 2012
97. MagLev - Summary
• Rabbit in hat trick
• Concurrent access to objects in distributed VM’s
• Proc’s can be persisted
• references enclosing environment’s variables
Rubyfuza - Cape Town - 2012
98. MagLev - Summary
• Rabbit in hat trick
• Concurrent access to objects in distributed VM’s
• Proc’s can be persisted
• references enclosing environment’s variables
• Continuations
Rubyfuza - Cape Town - 2012
99. MagLev - Summary
• Rabbit in hat trick
• Concurrent access to objects in distributed VM’s
• Proc’s can be persisted
• references enclosing environment’s variables
• Continuations
• Queue
Rubyfuza - Cape Town - 2012
100. MagLev - Summary
• Rabbit in hat trick
• Concurrent access to objects in distributed VM’s
• Proc’s can be persisted
• references enclosing environment’s variables
• Continuations
• Queue
• see example on github/maglev
Rubyfuza - Cape Town - 2012
101. Credits
Monty Williams
GemStone
http://maglev.github.com/docs/learn.html
https://github.com/MagLev/maglev
Conrad Taylor
Playing with Maglev the Ruby VM
http://www.conradtaylor.com/2011/11/08/playing-with-maglev-the-
ruby-vm/
Tim Felgentreff
Debugging on Steroids - what Ruby should learn from Smalltalk
http://blog.bithug.org/2011/09/maglev-debug
Rubyfuza - Cape Town - 2012
102. Questions
• Updates on Twitter @rudiengelbrecht
• Slides on SlideShare
• Source code on GitHub userid - rle
Rubyfuza - Cape Town - 2012
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
note the persistence by reachability\ncould also just add dhh\nshow persons[0].followers[0].followers[0].followers[1] is the reference to noob2\nalso persons[0].followers[1].followers[1] is reference to noob2\n
\n
\n
\n
\n
New design has following attribute - extra relation, bi-directional\nAlso a list of tweets\n
\n
\n
\n
\n
\n
\n
The new API is follows(person) instead of add_follower(person)\nowynn = Person.new("Owynn")\nunclebob = persons[0].followers[1]\nowynn.follows(unclebob) instead of below\nunclebob.add_follower(owynn)\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
To understand the utility of read locks, imagine that you need to compute the average age of a large number of employees. While you are reading the employees and computing the average, another user changes an employee&#x2019;s age and commits (in the aftermath of the birthday party). You have now performed the computation using out-of-date information. You can prevent this frustration by read-locking the employees at the outset of your transaction; this prevents changes to those objects.\nNOTEIf you have a read lock on an object and you try to write that object, your attempt to commit that transaction will fail. \n
To understand the utility of read locks, imagine that you need to compute the average age of a large number of employees. While you are reading the employees and computing the average, another user changes an employee&#x2019;s age and commits (in the aftermath of the birthday party). You have now performed the computation using out-of-date information. You can prevent this frustration by read-locking the employees at the outset of your transaction; this prevents changes to those objects.\nNOTEIf you have a read lock on an object and you try to write that object, your attempt to commit that transaction will fail. \n
To understand the utility of read locks, imagine that you need to compute the average age of a large number of employees. While you are reading the employees and computing the average, another user changes an employee&#x2019;s age and commits (in the aftermath of the birthday party). You have now performed the computation using out-of-date information. You can prevent this frustration by read-locking the employees at the outset of your transaction; this prevents changes to those objects.\nNOTEIf you have a read lock on an object and you try to write that object, your attempt to commit that transaction will fail. \n
Write locks are useful, for example, if you want to change the addresses of a number of employees. If you write-lock the employees at the outset of your transaction, you prevent other sessions from modifying one of the employees and committing before you can finish your work. This guarantees your ability to commit the changes.\n
Write locks are useful, for example, if you want to change the addresses of a number of employees. If you write-lock the employees at the outset of your transaction, you prevent other sessions from modifying one of the employees and committing before you can finish your work. This guarantees your ability to commit the changes.\n
Write locks are useful, for example, if you want to change the addresses of a number of employees. If you write-lock the employees at the outset of your transaction, you prevent other sessions from modifying one of the employees and committing before you can finish your work. This guarantees your ability to commit the changes.\n
Write locks are useful, for example, if you want to change the addresses of a number of employees. If you write-lock the employees at the outset of your transaction, you prevent other sessions from modifying one of the employees and committing before you can finish your work. This guarantees your ability to commit the changes.\n
create in one vm\nexecute in another vm\nhttp://maglev.github.com/docs/learn.html\n
create in one vm\nexecute in another vm\nFull credit to Monty / GemStone - see URL\nhttp://maglev.github.com/docs/learn.html\n\nThreads cannot be persisted by reference (because the JIT may have have compile methods to machine code, which cannot be ported across VMs), but we can use a trick.\nWhat did we do here? Well, we started a thread and created a continuation. A continuation captures the state of a computation, effectively copying the stack at the point it was created (We can see in the inspection output that the continuation has a reference to a copy of the Thread that created it). We assign the continuation to a global variable for convenience, and then commit it to the Stone repository.\n\n
create in one vm\nexecute in another vm\nFull credit to Monty / GemStone - see URL\nhttp://maglev.github.com/docs/learn.html\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n
abort transaction\nchange username and refresh in 2nd vm\nnot same as just returning cached instance in vm like other ORM&#x2019;s\n