Skip to content

Instantly share code, notes, and snippets.

@bkerley
Last active December 25, 2015 19:39
Show Gist options
  • Save bkerley/7029427 to your computer and use it in GitHub Desktop.
Save bkerley/7029427 to your computer and use it in GitHub Desktop.
map = @bucket.find_crdt 'my-map-key'
map
# alpha: register
# bravo: set
# charlie: map
# charlie.zulu: map
# charlie.zulu.yankee: counter
# delta: counter
map.registers #=> { :alpha => register("123") }, a Collection<Register> which is a kind of Hash
map.registers[:alpha] #=> register("123")
map.registers[:alpha].set "12345" # immediately issues MapOp(update("alpha", "12345"))
map.sets[:bravo] #=> set("abc", "def", "ghi")
map.sets[:bravo].add "abc" # noop? issue MapOp(update("bravo", SetOp(adds: "abc")))?
map.maps[:charlie].maps[:zulu].counters[:yankee].increment # issue MapOp(update("charlie", MapOp(update("zulu", MapOp(update("yankee", CounterOp(1)))))))
map.batch do |m|
m.counters[:delta].increment # enqueue update("delta", CounterOp(1)) on m (which is map in disguise)
m.maps[:charlie].maps[:zulu].flags[:xray] = true # enqueue add("xray", :flag) and update("xray", FlagOp(:enabled)) on m.charlie.zulu (which is map.charlie.zulu in disguise)
end
# issues enqueued updates in single MapOp
@mgodave
Copy link

mgodave commented Oct 17, 2013

Short answer is: no, the Java programming language and compiler don't work that way. It wouldn't compile unless you provide an interface containing those fields (at compile time) in your classpath. We don't have this info until runtime. You would have to have some sort of schema definition and/or an ide/build plugin to generate the code so it was available. The best you can do in the basic sense is: map.get("alpha").asRegister().set(...). Even if you had a schema available, any changes to the structure would require a recompile of the application.

@randysecrist
Copy link

+1 from me

@lenary
Copy link

lenary commented Oct 18, 2013

Yeah, I have some issues I should have raised earlier.

map.charlie.zulu.yankee.increment
# issue MapOp(update("charlie", MapOp(update("zulu", MapOp(update("yankee", CounterOp(1)))))))

This isn't how I see the key names at all. I see them as essentially if you have a map at key "charlie", the whole key you address as "charlie_map" or something.

Yes, working it out from the MapOp might make everything easier, but what if there's both a "charlie_map" and a "charlie_set", and you want to remove the element "foo" from "charlie_set", while keeping the field "foo" in "charlie_map". map.charlie.remove("foo") doesn't really get you anywhere.

But I'm not going to bitch without presenting an alternative:

map.map("charlie").map("zulu").counter("yankee").increment # => issue whatever
map.set("charlie").remove("foo") # => issue whatever
map.map("charlie").remove("foo", :map) # => issue something nicer

Indeed, symbols or strings work for me. So does even something like this:

map.charlie_map.zulu_map.yankee_counter.increment

where those types are defined, and regular ruby methods won't overlap with them.

I like your batch syntax, though I'd also like a blockless way of doing updates, as I had proposed in my gist here: https://gist.github.com/lenary/c054695204152972239e

you might add a method to my thing above like the following:

  def batch!(&blk)
    yield self # or do i instance_eval? I can't remember.
    self.send_operations!
  end

But I dunno.

remember, the interface needs to be the same both
a) whether or not the crdt exists; and
b) even if it does exist, whether or not you've fetched the structure first.

I think we're we're close.

@bkerley
Copy link
Author

bkerley commented Oct 18, 2013

@lenary Super thanks for filling me in that map entries are {name, type} => content and not name => {type, content}, that helps a ton, especially with the case that somebody puts an entry called batch or map or delete in a map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment