braid-spec icon indicating copy to clipboard operation
braid-spec copied to clipboard

New vocabulary for "Version"

Open canadaduane opened this issue 3 years ago • 29 comments

NOTE: This is not asking to change the header "Version:" to something else, merely the concept relating to the "Version:" header.

I want to elevate the discussion here to a proper braid-spec issue.

I was writing some code that used the term "Version" like the Braid spec uses it, but it caused the existing references to "version" or "patches" in the code to become ambiguous. If possible, we need clearer concepts.

I'll summarize by quoting from the above PR discussion:

[Duane]: I was introducing the term "patch" here, which obviously made the existing references to "patches" ambiguous. So I switched to using the term "Version" because that's what current braid calls the concept. Transaction seems a bit heavy-handed a term to me. It's more like a "PatchBundle" or something, but that is also confusing. Maybe we can get support for changing the term in the spec?

[Mike]: Seph raises the point that there are multiple concepts:

  1. A bundle of patches that occur atomically at a point in time
  2. The identifier for that bundle of patches
  3. The HTTP header in which you put the identifier for the bundle of patches

In my interpretation, the Braid spec names those concepts with these terms, respectively:

  1. Version
  2. Version ID
  3. Version: header

I think Seph is proposing something more like:

  1. Transaction
  2. Version ID
  3. Version: header

[Mike]: My analysis favors the former. They key issue that Seph raised is distinguishing (1) from (2). I think that is easy enough to do with "Version" vs. "Version ID". This has the advantage of making clear that the two concepts are related, and only differ in that one is the ID for the other. Switching to a different word like "Transaction" brings up the possibility that these concepts could be entirely different. A reader might think that a single transaction could hold multiple versions, for instance. There is also a question of whether the header should be named Version: or Version-ID:. One might argue that the header should be called Version-ID:, since it's containing an ID, and not the full data structure for a version. I sympathize with this argument, but the Version: header actually provides a dual role: it not only states the version ID, and also indicates the beginning of the Version data structure in the HTTP message. In any case, it'd be nice to resolve this so that we are using the same terms. I've been finding the conversions confusing when different people use different terms for the same things.

[Duane]: I think it's important to note that for whatever it is that we're describing, it's orthogonal to having a Version: header in it. For example, a subscription stream of these things can be used to update a clock and may not contain a version.

canadaduane avatar Mar 01 '21 19:03 canadaduane

What about the term "Update"?

An Update can optionally have a version (ID) An Update doesn't imply multiple versions An Update can be a snapshot An Update can contain a bundle of patches An Update is not as heavy-handed as a "transaction"

We can say that "a Braid subscription gives you a stream of updates" which is true whether there are versions involved, patches involved, or parents involved.

Colloquially, an "update" brings you up to date on the news. "What's the latest update?"

This also makes it easier to talk about the "Update headers" or the "Update body" (whereas, "Version headers" is very confusing, since "Version" is itself a header; and "Version body" is somewhat confusing, because a "Version" may not have a Version: header):

      Request:

         GET /chat
         Subscribe: keep-alive

      Response:

         HTTP/1.1 209 Subscription
         Subscribe: keep-alive

         Content-Type: text/plain                              | Update
         Content-Length: 28                                    |
                                                               |
         Mon Mar  1 12:32:34 MST 2021                          | | Body

         Content-Type: text/plain                              | Update
         Content-Length: 28                                    |
                                                               |
         Mon Mar  1 12:32:35 MST 2021                          | | Body

Here's another example, with the Version: header:

      Request:

         GET /chat
         Subscribe: keep-alive

      Response:

         HTTP/1.1 209 Subscription
         Subscribe: keep-alive

         Version: "ej4lhb9z78"                                 | Update
         Parents: "oakwn5b8qh", "uc9zwhw7mf"                   |
         Content-Type: application/json                        |
         Merge-Type: sync9                                     |
         Content-Length: 64                                    |
                                                               |
         [{"text": "Hi, everyone!",                            | | Body
           "author": {"link": "/user/tommy"}}]                 | |

         Version: "g09ur8z74r"                                 | Update
         Parents: "ej4lhb9z78"                                 |
         Content-Type: application/json                        |
         Merge-Type: sync9                                     |
         Patches: 1                                            |
                                                               |
         Content-Length: 53                                    | | Patch
         Content-Range: json=.messages[1:1]                    | |
                                                               | |
         [{"text": "Yo!",                                      | |
           "author": {"link": "/user/yobot"}]                  | |

canadaduane avatar Mar 01 '21 19:03 canadaduane

[Mike]: My analysis favors the former. They key issue that Seph raised is distinguishing (1) from (2). 

I'm trying to disambiguate between:

  1. State of the resource at some version ID (Resource at version A, resource at version B)
  2. Set of patches which take us from A to B in the time DAG

I want different words to disambiguate 2 from 1. We can't call them both "version" - thats confusing. "Here's version X" "You only gave me a patch!" "Yeah thats a version" "I wanted the whole document at version X!" ... etc.

I support Duane's suggestion of "update" for (2) because that term can refer to:

  • A single patch
  • A set of patches or
  • A copy of the whole document

And that makes good semantic sense.

josephg avatar Mar 02 '21 00:03 josephg

The term 'version' has always been a little confusing to me in this context (assuming I'm understanding everyone's latest thinking on it). Maybe someone can clear this up for me.

Let's say Alice and Bob are both in possession of "version" D:

    Alice        Bob
      |           |
      A           A
      |           |
      B           B
     / \          |
    E   C         C
     \ / \        |
      F   D       D

Their current state is clearly different. If a client connected to each of them and requested the state associated with version D, what would be reasonable for the client to expect? And if it's valid for "D" to mean different things to different actors, then how is it a useful distinction?

Alternatively, maybe this concept of "version" is not intended to be used in the context of requesting state? Or maybe we don't actually want to deal with requesting state at all (meaning that every client has to be capable of doing its own merge resolution)? I've been out of the loop for a bit, so I'm probably just missing something basic.

brynbellomy avatar Mar 02 '21 02:03 brynbellomy

Yeah it is a bit confusing. I feel like there’s some patch theory we need to write down in a document somewhere.

The way braid sees the world, version D refers to Bob’s document state. Alice’s document state is the merger of versions D and F. It’s named “D” “F” in the up to date version header. Note ‘D’ is also sometimes used as an identifier to name the patch set from C to D - which can be pretty confusing.

Some versioning systems have compact ways to express “the merger of A and B” - like vector clocks merge {a:1,b:2} and {a:2} into {a:2,b:2}. In braid there’s currently no way to express that in a parents field or in the up to date header.

josephg avatar Mar 02 '21 02:03 josephg

I had a chat with @toomim about this the other day. I think the best way to think about it is there's two different concepts at play: edges and nodes. Nodes are document versions, and edges show how one version turns into a new version with a set of patches.

Mike likes that there's enough overlap that the word "Version" can be overloaded to mean either an edge and a node in different contexts. I find it confusing (it adds cognitive load), and I'm obviously not alone. I might put together a PR to replace "version" with "update". Eg:

   If a GET request includes the Subscribe header, it will return a
   stream of versions; a new version pushed with each change.  Each
   version can contain either the new contents in its body, or a set of
   Patches.

becomes:

   If a GET request includes the Subscribe header, it will return a
   stream of updates; a new update pushed with each change.  Each
   update can contain either the new contents in its body, or a set of
   Patches.

(Each update corresponds to an edge. When an update is applied to the parent version(s), a new version is produced.)

josephg avatar Mar 06 '21 01:03 josephg

I'd like to move forward with this issue, because this is coming up in code I'm writing and I'd like to avoid code churn.

@toomim - As WG chair, what is our path forward here? Do you want to discuss this issue further in person? Do you have an alternate proposal? Do you agree with the proposal as written?

josephg avatar Mar 09 '21 05:03 josephg

Maybe we could use changeset for a set of patches, and version for ID of the changeset.

mitar avatar Apr 11 '22 20:04 mitar

All: I'm sorry for dropping the ball as "chair" and letting this issue sit. I should have relinquished that responsibility, as I wasn't able to keep up.

For now, let me take off the "chair" hat, and just comment as a group member.

As a member, I've been convinced by the arguments of @canadaduane and @josephg, and agree with calling the set of changes that update state to a new version an update. I think we should update the spec as duane suggests above, and offer to put together a draft to make this happen.

I also see two additional questions coming up in this thread:

  • @josephg raises the distinction of the "State of the resource at some version ID". Is this a version, or a snapshot?
  • @brynbellomy raises the distinction between a Version ID and the merger of multiple Versions. Is the merger of multiple versions also a version, or is it a "merger of versions"?

These concepts are all related, and it's probably worth thinking them through together so that we know they are coherent.

Perhaps we can define them as something like:

  1. Version ID: a single ID for a single edit, e.g. "8zsn2ha"
  2. Version: a set of IDs specifying a point in time, e.g. "8zsn2ha" or "8zsn2ha", "nsh3aj"
  3. Snapshot: the contents at a version, e.g. <html>Hello world!</html>
  4. Update: a data package to update a peer from one version to another

(As for "transaction", I think we might like to reserve the term for future use, to refer to a set of updates in a branch that should all be commited or discarded as an atomic unit.)

toomim avatar Aug 19 '23 00:08 toomim

Note that point (2) above also implies that our Version: header could potentially hold multiple Version IDs.

This is useful, for instance, in the case that a server has received two parallel edits:

   a
  / \
 b   c

And now a new client wants to receive the current snapshot. The server could provide it like:

HTTP/1.1 200 OK
Version: "b", "c"
Content-Length: 11

Hello world!

Without specifying multiple IDs inside a single Version:, the server would have to respond with the three separate updates. It would be unable to give a name for the single snapshot that contains both leaf versions b and c.

toomim avatar Aug 19 '23 00:08 toomim

I've drafted these changes in https://github.com/braid-org/braid-spec/pull/113. A review would be appreciated!

toomim avatar Aug 19 '23 02:08 toomim

(Chair:) The discussion on PR #113 found consensus on Version and Current-Version, but led to some new ideas for Version ID, Update, and Snapshot.

The task is now to find consensus on these terms. We have some suggested alternatives:

Alternatives to Version ID:

  • Edit ID
  • Event ID
  • Operation ID
  • Patch ID

Alternatives to Update:

  • Event
  • Change
  • Mutation

Alternative to Snapshot:

  • (Representation) State

Argument for Event:

  • @cxres: I use the term event instead of update because this is the term used in relativity in physics. A thing at a given space & time is an event.

Argument for Event ID:

  • @dglittle: I second the use of the term event— for me that term fits better in the edit ID. Because that has a time and a place. The version is an arbitrary collection of events that have been observed.

Argument against Update:

  • @cxres: Time-travel can go backwards. Asking for info in the past isn't an "update", it's a "backdate".

toomim avatar Oct 18 '23 21:10 toomim

While this might not affect Braid, but it matters for notifications in general:

A POST on a resource might not necessarily change the representation of the resource. A resource can define its own arbitrary semantics for POST. However, one might still want to emit (and receive) a notification for that POST. In such a case, Update, Mutation and Change are less suitable as terms than Event.

CxRes avatar Oct 19 '23 01:10 CxRes

Ah, you're talking about the architecture of synchronization that I think of as "State Machine Synchronization." This architecture is characterized by:

  • Semantic events are passed around
    • Across components via a message bus
    • And/or over a network to other peers
  • Each event defines a transition in a state machine
    • All possible mutations to all aspects of state must be specified
    • Each peer must implement the state transitions perfectly for all possible mutations

POST is used for this type of synchronization. Typical example:

  • Client POSTs a /new_message event
  • Server interprets this to mutate the /messages state with a new message

This is simple enough when you only have one event type (the new_message event) and only one peer that needs to implement the meaning of it (the server). However, once you start introducing multiple peers, and more complex state, this architecture really sucks.

Let's say that you want to add a new state transition, like /modify_author. Now you need to implement that transition on all peers. What if they are built in different programming languages? And what happens if two peers send the event at the same time? Now you have to implement the merge semantics, in each peer, customized to the specific needs of your app's semantics. What if you want an offline mode? Good luck doing OT or CRDT. This starts to suck really bad.

The problem is entangling the semantics of the application with the synchronization algorithm. The sync algorithm ends up being custom-implemented for the application. And any change to the data schema, or the sync algorithm, has to be faithfully re-implemented in all components of the system, or they get out of sync with race conditions, and risk data corruption. This gets super nasty.

This is what we're solving by implementing general state synchronization algorithms, that don't depend on any application's semantics. Mutations become syntactic patches to general state. Merge semantics become a general merge-type.

This is what React solved for front-end development. Instead of each UI component implementing a state machine (like you had to do with backbone.js before react), the UI just became a function on top of general synchronized state.

This is a big key to the state synchronization revolution— moving from Synchronized State MachinesFunctions on Synchronized State.

So now, back to your point on general notification— yes, in the model of State Machine Synchronization, it's true that peers subscribe to semantic events, rather than syntactic changes to state. However, it turns out that any semantic event can be expressed equivalently with a syntactic state mutation. For instance, in the POST /new_message example, the clients could simply subscribe to changes to the /messages state directly. Furthermore, they could also send new messages by patching the /messages state, rather than going through the indirect POST /new_message state. Doing so simplifies the architecture, makes it more peer-to-peer, and allows general libraries to handle everything—your app doesn't need server code anymore!

In conclusion, I think POST is going the way of the dodo, and so is the "Event" style of synchronization, and I think we'll do better to think in terms of Updates, Changes, or Mutations to state.

We should still support the event information using a header, like "Mutation-Description: new_message(...)", in order to ease interoperability with legacy systems built as synchronized state machines, but I think our children will thank us if we give them this higher-level abstraction to work with.

toomim avatar Oct 19 '23 03:10 toomim

All of the above not withstanding, if I subscribe to a HTTP resource (for notifications in general, not necessarily Braid's state synchronization) and someone does a POST on that resource, I would rather like to be informed of the (irrespective of whether it mutates the resource or not). Thus, the trigger of the notification is an event that may or may not result in a mutation of that resource (that information could be useful, for example, "to know" to start observing a newly created resource elsewhere).

CxRes avatar Oct 19 '23 03:10 CxRes

Can you illustrate that with a concrete example? Can you show me the network messages that you're envisioning? What would be the POST to the server? What would the notification message look like that you want back? What would you do with that message?

toomim avatar Oct 19 '23 04:10 toomim

https://cxres.github.io/prep/draft-gupta-httpbis-per-resource-events.html#figure-7

I know after that message that I might want to look at /foo.

CxRes avatar Oct 19 '23 04:10 CxRes

So this notification says:

Method: POST
Date: Sat, 1 April 2023 10:12:14 GMT
Event-ID: 1235
Content-Location: /foo

Is this just echoing the post request back to the clients? If there was a body, would that body go into the notification too?

And in this scenario, are you imagining the post to /foo would result in a change to /foo's state? I figured it was going to mutate state somewhere else.

I'm trying to understand the scope of the data you want back on the client, and why, by getting more of the scenario fleshed out.

toomim avatar Oct 19 '23 04:10 toomim

  1. The request by notification receiver is (watching /):
GET / HTTP/1.1 
Accept-Events: PREP 
  1. Now someone POSTs to /:
POST / HTTP/1.1 
Slug: foo

<body>
  1. So / creates /foo and sends a message to the notification receiver watching /:
--random-boundary  <- From the Previous Message (do not include)
                   <- Empty part response headers & blank line
Method: POST
Date: Sat, 1 April 2023 10:12:14 GMT
Event-ID: 1235
Content-Location: /foo
--random-boundary

Now, it is not required here that state of / changes (it could, but that's implementation dependent). But, as someone watching /, I would want to know that I now can go and do something with /foo.

The body of the POST request is intended for /foo, so that will not be echoed here (as defined in the rfc822 mini spec). But for another representation/semantics paring, one could define that to be the case (it would be a strange thing to do, especially when thinking about security).

CxRes avatar Oct 19 '23 05:10 CxRes

Thank you, that clarifies.

So I'm hearing that you don't think this is needed for State Synchronization or Braid-HTTP, but that you want it for notifications in general. Do you think Braid-HTTP should support or relate to such general notifications?

I have been thinking we don't need general notification streams in Braid or HTTP, because we can express everything as State Synchronization.

The way I'd do the above as State Synchronization is:

  1. Request a subscription to the index state at /:
GET / HTTP/1.1 
Subscribe: true

...the server responds with the current index of articles:

HTTP/1.1 209 Subscription
Subscribe: true

Content-Length: 2

[]

...which happens to be empty thus far.

  1. Now someone appends the /foo article to the index at /:
PUT /
Content-Range: json [0:0]
Content-Length: 423

[{"slug": "/foo", ...}]
  1. And / creates /foo and updates all subscribers with the new state of /:
Content-Range: json [0:0]
Content-Length: 423

[{"slug": "/foo", ...}]

This expresses the same behavior using State Sync, and we don't need general notifications. If you think we do need general notifications, I'd be curious why.

I think constraining realtime updates to State Synchronization is more in-line with HTTP and ReST. Remember that the big advantage of ReST was that it constrained how people program networked computing systems:

5.1 Deriving REST

The design rationale behind the Web architecture can be described by an architectural style consisting of the set of constraints applied to elements within the architecture. By examining the impact of each constraint as it is added to the evolving style, we can identify the properties induced by the Web's constraints. Additional constraints can then be applied to form a new architectural style that better reflects the desired properties of a modern Web architecture. This section provides a general overview of REST by walking through the process of deriving it as an architectural style. Later sections will describe in more detail the specific constraints that compose the REST style. https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

Following these constraints meant that your system was more likely to be scalable, interoperable, etc.

Likewise, I think the constraint of State Sync has better properties than a system running on general event streams.

toomim avatar Oct 19 '23 08:10 toomim

So I'm hearing that you don't think this is needed for State Synchronization or Braid-HTTP, but that you want it for notifications in general. Do you think Braid-HTTP should support or relate to such general notifications?

What I am saying is that we need a common language because the updates to effect State Synchronization is a proper subset of general notifications. Having a notion of events, and that not all events mutate the state of a resource but instead affect other resources, has to be a part of language. This (the language part, the thing we are discussing in the issue) is independent of how you want to do state synchronization.

CxRes avatar Oct 19 '23 09:10 CxRes

Thank you for clarifying. I can now see more specifically now where my disagreement lies:

What I am saying is that we need a common language because the updates to effect State Synchronization is a proper subset of general notifications.

Although it is true that State Sync can be expressed through a general notification mechanism, that does not mean that it should be.

Consider that HTTP could be expressed on top of a general RPC standard. But it is not. HTTP's strength is in being constrained in the right ways, as Roy Fielding articulates in his thesis.

My argument is that State Sync is an advantageous set of constraints, because it decouples synchronization from application logic. This allows, for instance:

  • dumb middleboxes to cache state for applications without knowing anything about the applications (enabling SOLID pods, for example)
  • advanced synch algorithms (OT, CRDT, Time Machines) to be developed in general ways that apply to any application

I don't see a win for programming with general event notification streams, and I've thought about this quite a bit. I would be very curious to hear if there is a practical reason for having such a standard built into Braid, other than "it's possible to do." Possible != good.

To take the argument further, consider that it's also possible to implement a notification stream on top of state synchronization. Imagine we create some /current_event state, and mutate it over time:

Request:

  GET /current_event
  Subscribe: true

Response:

  HTTP/1.1 209 Subscription
  Subscribe: true

  Content-Length: xx
  Version: "1"

  {"event": "new_message", "payload": { ... }}

  Content-Length: xx
  Version: "2"

  {"event": "change_author", "payload": { ... }}

  Content-Length: xx
  Version: "3"

  {"event": "edit_message", "payload": { ... }}

Thus, although you might see a world in which Event Streams > State Synchronization, it's also equally valid to see State Synchronization > Event Streams. Do we need to build one on top of the other? I don't see a pragmatic benefit to doing so. But I would love to be shown something I've missed.

toomim avatar Oct 19 '23 09:10 toomim

(This is in response not to the last comment but still the one previous to it)

Now, as to the side-tracked issue, I do not like the idea of "index" resources/containers that are semantically different from regular resources. I am having the same fight over at Solid. Any resource should have its own representations and should be a point from where you can define contained resource (the notion of indexing/containment is needed primarily for inherited access control). So here if I access / from a browser I really want to GET a text/html but then also be able to POST to it, which can have side effects. And I really do not want to couple the state of /foo with / (as opposed to knowledge of /foo in /) which once created is its own thing.

The thing about the constraints is that you have the burden to prove that without the proposed constraint, the property that you desire cannot be effected (something Roy does rather admirably in his thesis). I do not think this qualifies!

Now, I must admit I have a bit of an unfair advantage when reasoning about this (something we can speak about next time), which is why I am so confident.

CxRes avatar Oct 19 '23 09:10 CxRes

I do not like the idea of "index" resources/containers...

Can you be more specific with your problem in this paragraph? I don't understand. I would greatly appreciate examples.

The thing about the constraints is that you have the burden to prove that without the proposed constraint, the property that you desire cannot be effected

The constraint is separating application logic (which defines the state transitions that are allowed, ie. the state machine) from the synchronization algorithm and protocol. If you don't decouple them, you lose the property of supporting dumb middleboxes (e.g. a CDN or proxy) that interpret patches, and store and serve state without implementing the application's state machine.

Is that clear?

Update: Consider that your POST example relies on a server to implement the state machine for the Slug: header. Everyone in the network is forced to consult the server to learn what's changed, unless they implement the state machine too. My alternative expressed as state sync, on the other hand, enables proxies to update their state without consulting a server, and without implementing any application logic.

toomim avatar Oct 19 '23 10:10 toomim

There are so many different threads of conversation here that I fear I cannot respond to all of them in the time before the @toomim bot responds. It will have to wait for me to dis-entangle all the issues.

CxRes avatar Oct 19 '23 10:10 CxRes

Feel free to use the mailing list, which supports actual threading.

toomim avatar Oct 19 '23 10:10 toomim

Feel free to use the mailing list, which supports actual threading.

That can only deal with space issue, not the time issue. (Also, I am watching a Cricket World Cup match on the side).

CxRes avatar Oct 19 '23 10:10 CxRes

Very interesting read. From what I gather, the rationale behind https://cxres.github.io/prep/draft-gupta-httpbis-per-resource-events.html is more about being able to observe requests made towards a server in a general way, so that you can decouple different systems ("oh, somebody made a POST against resource X, let me check if anything changed at X"). This looks to me like something which is easy for a server to add to enable such decoupling.

While state syncing is much more integrated into the server and generally goes two ways. So I also do not see how those two things could be based on each other.

mitar avatar Oct 19 '23 10:10 mitar

While state syncing is much more integrated into the server and generally goes two ways. So I also do not see how those two things could be based on each other.

Despite how the above discussion might appear, I believe that both @toomim (bot? ;)) and I see a lot of common ground. Synchronization will always need a notifications mechanism (ie you subscribe to a (set of) resource for updates). But does that mean the notification mechanism has to be specific to state sync or can a general notification mechanism (which can be used to listen for events in general) be constrained for it (and is still desirable). I very much think the latter is true.

But this discussion has spread all over the place from the original discussion about terminology.

CxRes avatar Oct 19 '23 10:10 CxRes

A state only comes about due to events. Now there are three kinds of events:

  • An event that does not modify state
  • An event that modifies the state of the resource
  • An event that modifies the global state (such as by creating resources). This is a mutation too (in HTTP semantics you do for either only for requested resource or "slug"-like resource or both, the examples I was suggesting are in particular for "slug"-like resource).

That you might not want to include events of type 1 and 3 in Braid does not mean they go away and should be banished from a discussion about language itself. Actually, doing so will later just constrain your design space.

Again, whether you choose to include event or changes in Braid, the Braid "notification" will always be a proper subset of general notifications. If you choose NOT to build a/build on top of a general notifications mechanism, that can only be justified on grounds of simplicity/efficiency/practicality, not because the two are distinct.

All things said, this was a discussion about vocabulary and not design. To the extent that this discussion is conflating the two is an epistemic error.


To address some specific comments:

I think POST is going the way of the dodo, and so is the "Event" style of synchronization, and I think we'll do better to think in terms of Updates, Changes, or Mutations to state.

I'll take a wager that it is not as long as HTTP exists (so at least another decade at a minimum). So I'll wait until you obsolete RFC9110.

I do not like the idea of "index" resources/containers...

Can you be more specific with your problem in this paragraph? I don't understand. I would greatly appreciate examples.

You are actually adding (hypermedia) application semantics on top of HTTP by constraining / to act as an index/container of sub-resources in order to support synchonization. This couples the two resources to emit a global change, locally from the resource. Once you have created /foo, it should become independent of /. Equally to doing PUT / [{"slug": "/foo", ...}], I could now PUT /foo. It would be imho absolutely wrong to emit that as an event on /, which is presumable what your design will do to keep states synchronized (since I am now watching / through /foo). If you do not smuggle state through this backdoor for PUT /foo, you are back to my POST example.

Furthermore, the notion of having resources that are for "content" and resources that act as "index" gives me the creeps. It makes applications that support ordinary data and where schema of data is not known in advance (such as Syntropize) orders of magnitude harder, because I need to treat resources (in a logical grouping) in two different ways.

The constraint is separating application logic (which defines the state transitions that are allowed, ie. the state machine) from the synchronization algorithm and protocol.

My argument is that State Sync is an advantageous set of constraints, because it decouples synchronization from application logic.

I support this in general. But in the example you have created in https://github.com/braid-org/braid-spec/issues/102#issuecomment-1770368062, this is not what you are doing. You are actually adding (hypermedia) application semantics on top of HTTP by constraining / to act as an index/container of sub-resources in order to support synchronization (which is what my example does too, but I'm stating that as possibility not inevitability). That, this / state has indefinite access to changes of representation of /foo for emitting state changes is a bridge too far for me.

My larger claim: Nothing in adopting a general notifications mechanism (and then imposing constraining semantics) on it for state sync precludes this decoupling.

To take the argument further, consider that it's also possible to implement a notification stream on top of state synchronization. Imagine we create some /current_event state, and mutate it over time:

it's also equally valid to see State Synchronization > Event Streams.

If you constrain this discussion for updating client (or a peer in client role) part and not the magic that server does with multiple versions in creating a consistent state before sending out updates, which is what the substance of this discussion is about, this claim is a logical impossibility (see the top of this comment)! At best (state sync <= general notifications) since updates for sync are a kind of notifications amongst other types of notifications. Also see above the problems highlighted in your example.

It seems to me that you are conflating two orthogonal things as part of one mechanism -- creating consistent state and updating clients with that state. A design that conflates both this way will only create complexity and be less robust (This itself is a larger issue which needs a course and not a thread).

Consider that your POST example relies on a server to implement the state machine for the Slug: header. Everyone in the network is forced to consult the server to learn what's changed, unless they implement the state machine too. My alternative expressed as state sync, on the other hand, enables proxies to update their state without consulting a server, and without implementing any application logic.

I am not assuming application semantics here. I am taking a rather common use case for POST and stating that I would like to be informed of it. I really don't understand this objection, so you might want elaborate. Between listening to the resource and the Content-Location header, any intermediary has enough to update state by adding a new location. It is not as though other write requests (PUT/PATCH/DELETE) can be completed without walking all the way back to the origin server.


As I worked through answering this, I stand corrected wrt my comment to @mitar. I believe the gap between us is greater than expected after all. This is not a good media to bridge that gap as it takes a lot of time to parse through and reply to (a luxury I do not have right now, given 10-100x costs of doing the same things as you, as you are well aware from our private discussions). Apart from the fact that discussions hijacks the title/purpose of the issue (of ontology where I stand firm!), I also have a sense of being impulsively, even if very politely, bombarded with conflated issues which distracts from the stated issue. I want to say more but for the sake of bandwidth and sanity I will refrain!

CxRes avatar Oct 21 '23 04:10 CxRes