Isn't stateful lifts whole claim to fame? I mean view first and snippet is nice philosophy but probably not to hard to do on top of play. So it seems stateful is what makes lift lift?
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
Before I leave the topic of state, I have one other kinda crazy idea... My belief (read: could be wrong) is that there is no fundamental reason why the server-side state should reside in memory. Perhaps if there was a hook that allowed it to be placed elsewhere, you could swap out the running server and the next one would just continue with the previous run's state. Furthermore, this state could be shared by multiple instances simultaneously to form a cluster. I'm not suggesting this is anything practical, but I'm just wrestling with the fundamentals of server-side state and security in my mind. :)
As I've thought this over some more, I'm not really keen on the idea of solving this with documenting stateless implementation alternatives as you mentioned. I'd like to figure out how to make Lift hit these goals well without chunking tried-and-true features.
At the risk of having multiple threads within a thread, perhaps first we should articulate what would be the goal for Lift with respect to modern cloud deployment. Here is my stab at it:Lift applications are:Cloud-ready - Application updates can be deployed continuously without downtime facilitated across multiple server instances.
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
Before I leave the topic of state, I have one other kinda crazy idea... My belief (read: could be wrong) is that there is no fundamental reason why the server-side state should reside in memory. Perhaps if there was a hook that allowed it to be placed elsewhere, you could swap out the running server and the next one would just continue with the previous run's state. Furthermore, this state could be shared by multiple instances simultaneously to form a cluster. I'm not suggesting this is anything practical, but I'm just wrestling with the fundamentals of server-side state and security in my mind. :)I've been thinking about trying this out too, but one place where this would not work, or at least would need more work to work around, is for cases where we are invoking a server side closure/function that changed on the next deployment, in other words, if we store the key -> function on something like redis, but the actual code for the function changed, we would be calling something that is not correct/valid any more.
As I've thought this over some more, I'm not really keen on the idea of solving this with documenting stateless implementation alternatives as you mentioned. I'd like to figure out how to make Lift hit these goals well without chunking tried-and-true features.I would disagree, documenting how to use Lift for your particular use case is a good thing, and if it means to know when not to use certain very popular features of Lift, it is much better than then going around saying that Lift failed in xyz situation, when the problem was lack of knowledge.
At the risk of having multiple threads within a thread, perhaps first we should articulate what would be the goal for Lift with respect to modern cloud deployment. Here is my stab at it:Lift applications are:Cloud-ready - Application updates can be deployed continuously without downtime facilitated across multiple server instances.Here is where we need to really define what we are looking for, cloud ready can mean anything, and I don't think Lift isn't cloud ready, I run a personal Lift app on the cloud, I have another client who I have been working for over a year and also has their app on the cloud. This would feel just how all of the sudden people want to be "reactive". Now, continuous deployment is an actual technique to explore and without changing anything in Lift now, you could do that. Not too long ago Tim mentioned the idea of green blue deployment, which is something I had heard before but never took the time to read about, it turns out it is a very simple concept, you have two sets of servers, only one live where all you users go to, if you have an update, you deploy it to the other server group, and once it is ready, change your load balancer to start redirecting traffic to that new set of servers, if you still have users left on the "old" codebase, you can push a message/notification asking users to either reload the page, or log out/login (up to you), so they go to the new server group with the new code. and that's it, nobody lost any data, and the users were able to decide *when* to be disrupted and move to the new server group. You could even have several groups of servers if you wanted to deploy several times a day.
--
Right I meant goals. I guess one benefit of state is also the function names and every thing built on it like shtml. But it's interesting thought, that it's really flowing from another goal of security
I guess you can always build stateful on stateless but not opposite (proof, http is stateless and lift and servlets is built on it)
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For serializing functions, I think you should keep an eye on spores.
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
Joe,
I don’t think that I’m thrown off by the idea of modules. I think what I’m having trouble with is understanding what you mean by clustering. To me, clustering refers to using multiple servers (a cluster) to provide a service. You could be doing that for many reasons, all of which potentially have different requirements when it comes to managing state. It seems like what you’re most interested in is reliability: that when a node is lost, either through failure or due to an intentional action, your clients are unaffected. I’m also getting the impression that when you say clustering, what you mean is replication of state within a cluster.
One thing I was trying to explain is that there are multiple types of state in a Lift app (if you choose to hold state on the server at all) some of the them can be replicated while others can’t.
If you want to: Replicate user login information so the user won’t be logged out when their node goes away, then yes this is possible with Lift now. You can use a ContainerVar to store the user information and set up HttpSession replication. Log in, kill the node, be amazed!
If you want to: Replicate any arbitrary values between nodes, also a yes. Same process as above.
If you want to: Make it so that each and every one of Lifts features can be used without users being affected when a node disappears, then unfortunately the answer is no. Here is the main reason. Registering functions in the session and assigning them a secure id makes SHtml very secure and powerful. Unfortunately, those functions cannot be reliably serialized, which means that they can not be replicated between nodes.
Of course, there is no reason that you need to use SHtml, and while I still find plenty of situations where SHtml is useful, the more I move to apps using libraries like ReactJS, the less reliant I’ve become on it. You can absolutely develop an awesome Lift app that doesn’t use SHtml at all, stores session related data in a ContainerVar, and ties into the container’s (i.e Jetty’s) session replication. Configure that all correctly and it should fail over seamlessly.
That said, this process could absolutely be improved. Like I said, configuring container based session replication sucks. If replication of SessionVars was built into Lift then the whole dance with ContainerVar / container session replication could be avoided and that would also have the benefits of
(a) making what can be replicated in a Lift app, and how to accomplish replication, more obvious
(b) making replicated Lift apps more portable
(c) making session replication work outside of a servlet container (netty) and
(d) making me happy to never have to curse about container replication again.
So my question to you is, what exactly are you trying to accomplish? If it’s replicating data, then you have a mechanism at hand, and if you want to improve on it, that would be awesome, but it should work either way. If your need goes beyond that, then I think you should really consider how adopting another framework would help. To my knowledge, there is nothing that exists that offers the type of functionality you get from SHtml in Lift in a way that can seamlessly be replicated between nodes.
-Dave
def serialize(obj: AnyRef): Array[Byte] = {
val baos = new java.io.ByteArrayOutputStream()
val oos = new java.io.ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
baos.toByteArray
}
class C1 {
var variable = "hello world"
val func = () => println(variable)
}
class C2 extends Serializable {
var variable = "hello world"
val func = () => println(variable)
}
serialize(new C1().func) // <-- CRASHES
serialize(new C2().func) // <-- WORKS FINE
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.
--
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+unsubscribe@googlegroups.com.
import java.io._
def serialize(obj: AnyRef): Array[Byte] = {
val baos = new ByteArrayOutputStream()
val oos = new ObjectOutputStream(baos)
oos.writeObject(obj); oos.close(); baos.toByteArray
}
def deserialize[T](arr: Array[Byte]): T = {
val ois = new ObjectInputStream(new ByteArrayInputStream(arr))
try {ois.readObject().asInstanceOf[T]} finally {ois.close()}
}
class C2 extends Serializable {
var variable = "hello world"
val func = () => println(variable)
}
object FMap {
var map = Map[String, () => Unit]()
}
val uid = java.util.UUID.randomUUID().toString
FMap.map = FMap.map + (uid -> new C2().func)
FMap.map(uid)()
FMap.map = deserialize[Map[String, () => Unit]](serialize(FMap.map))
FMap.map(uid)()
--
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
def render(): NodeSeq = {
var amount = 0
<button onclick={SHtml.ajaxInvoke(() => {amount += 1})}>Increment</button> ++
<button onclick={SHtml.ajaxInvoke(() => {amount -= 1})}>Decrement</button>
}
def test(): Unit = {
import java.io._
def serialize(obj: AnyRef): Array[Byte] = {
val baos = new ByteArrayOutputStream()
val oos = new ObjectOutputStream(baos)
oos.writeObject(obj);oos.close();baos.toByteArray
}
def deserialize[T](arr: Array[Byte]): T = {
val ois = new ObjectInputStream(new ByteArrayInputStream(arr))
try {ois.readObject().asInstanceOf[T]} finally {ois.close()}
}
val fmap = collection.mutable.Map[String, () => Unit]()
// Enclosing class must be serializable (otherwise will get: "java.io.NotSerializableException: $anon$1"):
new AnyRef with Serializable {
var amount = 0
def render() = {
fmap += ("incF_9389120" -> (() => {amount += 1; println(s"Inc to $amount")}))
fmap += ("decF_1837599" -> (() => {amount -= 1; println(s"Dec to $amount")}))
}
}.render()
// Serializing/deserializing the single functions:
val singleIncFuncDeserialized = deserialize[() => Unit](serialize(fmap("incF_9389120")))
val singleDecFuncDeserialized = deserialize[() => Unit](serialize(fmap("decF_1837599")))
println("Wrong: changing different variables:")
singleIncFuncDeserialized()
singleIncFuncDeserialized()
singleDecFuncDeserialized()
singleDecFuncDeserialized()
// Serializing/deserializing the whole map:
val wholeMapDeserialized = deserialize[collection.mutable.Map[String, () => Unit]](serialize(fmap))
println("Correct: changing the same variable:")
wholeMapDeserialized("incF_9389120")()
wholeMapDeserialized("incF_9389120")()
wholeMapDeserialized("decF_1837599")()
wholeMapDeserialized("decF_1837599")()
}
test()
This is the output:
scala> test()
Wrong: changing different variables:
Inc to 1
Inc to 2
Dec to -1
Dec to -2
Correct: changing the same variable:
Inc to 1
Inc to 2
Dec to 1
Dec to 0
Macros are what I am missing. Here is the plan I'm going to try fleshing out soon (I hope):
The idea is to store what is minimally needed. Hence, I'm throwing out concerns of serializing functions and the mess that could produce. I want to only serialize the GUIDs put into the pages served and what they were attached to. In the case of comets, I don't suppose this is difficult because they always have a type and optionally a name. For ajax stuff, they are always associated with an anonymous function which with vanilla Scala isn't readily referenced by something like a name. With macros, we could generate a static GUID for each function. This way when we have a GUID miss and go to the store, we can find it and invoke the anonymous function called.
As I mentioned earlier, I have no interest in this working for page loads served from different versions of the app. This approach breaks in that case, which is the right behavior (assuming that particular function was recompiled).
The first thing I need to identify is a good replicated in memory key/value store, preferably a JVM one which will just be an added dependency. This would keep the deployment/ops of this feature simple. (I still see this as a module in case you wanted to back this with redis or whatever). Furthermore, this would always clean up nicely when you deploy a new version, ensuring you don't accidentally retain dead state.
Somebody hand me my helmet.
Joe
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+unsubscribe@googlegroups.com.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+unsubscribe@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
<body data-lift-session-id="F384714483040YW2B5P" data-lift-gc="F384714483041O5VFVI">
Code: <a href="http://github.com/lift" rel="nofollow" target="_blank" onmousedown="this.href='http://www.google.com/url?q\75http%3A%2F%2Fgithub.com%2Flift\46sa\75D\46sntz\0751\46usg\75AFQjCNHXwotOnPyMot2hshAC8YHAt_dmzQ';return true;" onclick="this.
To unsubscribe from this group and all its topics, send an email to liftweb+unsubscribe@googlegroups.com.
--
--
Lift, the simply functional web framework: <a href="http://liftweb.net" rel="nofollow" target="_blank" onmousedown="this.href='http://www.google.com/url?q\75http%3A%2F%2Fliftweb.net\46sa\75D\46sntz\0751\46usg\75AFQjCNEmJwOCJGtjWwlxKbLBvr-O
...
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/KHjbjev8A0E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.