[47°09′01″S, 126°43′57″W] Raw reading: 0x637BD8C1, offset +/-5
[47°09′57″S, 126°43′00″W] Reading: 0.10 Sv
[47°09′19″S, 126°43′31″W] Reading: 1.88 Sv
[47°09′37″S, 126°43′02″W] Reading: 0.44 Sv
[47°09′46″S, 126°43′43″W] Reading: 0.84000 PPM
[47°09′59″S, 126°43′47″W] Reading: 0.34 Sv
[47°09′11″S, 126°43′07″W] Raw reading: 0x63771F61, offset +/-4
Feature Comparison
⌘ Read more
[47°09′13″S, 126°43′26″W] Reading: 0.58000 PPM
[47°09′35″S, 126°43′17″W] Reading: 1.56000 PPM
[47°09′25″S, 126°43′27″W] Reading: 0.26000 PPM
[47°09′37″S, 126°43′21″W] Reading: 1.73 Sv
[47°09′05″S, 126°43′27″W] Reading: 0.67 Sv
[47°09′34″S, 126°43′51″W] Reading: 0.20 Sv
[47°09′51″S, 126°43′04″W] Reading: 0.32 Sv
[47°09′01″S, 126°43′29″W] Reading: 1.02 Sv
[47°09′37″S, 126°43′56″W] Reading: 1.84000 PPM
[47°09′05″S, 126°43′57″W] Raw reading: 0x6367A1C1, offset +/-3
[47°09′58″S, 126°43′14″W] Raw reading: 0x63661801, offset +/-1
[47°09′29″S, 126°43′57″W] Reading: 0.67 Sv
[47°09′16″S, 126°43′15″W] Reading: 1.94 Sv
[47°09′29″S, 126°43′30″W] Reading: 0.64 Sv
[47°09′15″S, 126°43′32″W] Raw reading: 0x635F8081, offset +/-2
I was inclined to let this go so as not to stir anything up, but after some additional thought I’ve decided to call it out. This twt:
is exactly the kind of ad hominem garbage I came to expect from Twitter™, and I’m disappointed to see it replicated here. Rummaging through someone’s background trying to find a “gotcha” argument to take credibility away from what a person is saying, instead of engaging the ideas directly, is what trolls and bad faith actors do. That’s what the twt above does (falsely, I might add–what’s being claimed is untrue).
If you take issue with something I’ve said, you can mute me, unfollow me, ignore me, use TamperMonkey to turn all my twts into gibberish, engage the ideas directly, etc etc etc. There are plenty of options to make what I said go away. Reading through my links, reading about my organization’s CEO’s background, and trying to use that against me somehow (after misinterpreting it no less)? Besides being unacceptable in a rational discussion, and besides being completely ineffective in stopping me from expressing whatever it is you didn’t like, it’s creepy. Don’t do that.
[47°09′06″S, 126°43′31″W] Reading: 1.46000 PPM
[47°09′40″S, 126°43′47″W] Raw reading: 0x635C26B1, offset +/-1
[47°09′22″S, 126°43′32″W] Raw reading: 0x635983B1, offset +/-4
[47°09′53″S, 126°43′49″W] Raw reading: 0x63595982, offset +/-2
[47°09′54″S, 126°43′16″W] Reading: 1.80 Sv
[47°09′54″S, 126°43′11″W] Reading: 1.00 Sv
[47°09′42″S, 126°43′03″W] Reading: 0.03 Sv
[47°09′03″S, 126°43′38″W] Reading: 1.77 Sv
[47°09′56″S, 126°43′23″W] Reading: 1.33 Sv
[47°09′43″S, 126°43′39″W] Reading: 1.80000 PPM
[47°09′42″S, 126°43′31″W] Reading: 1.02000 PPM
[47°09′45″S, 126°43′33″W] Reading: 1.77000 PPM
[47°09′27″S, 126°43′18″W] Raw reading: 0x63425551, offset +/-5
[47°09′20″S, 126°43′37″W] Reading: 1.33 Sv
[47°09′52″S, 126°43′48″W] Reading: 1.64 Sv
[47°09′14″S, 126°43′24″W] Reading: 0.34000 PPM
[47°09′52″S, 126°43′43″W] Reading: 1.80000 PPM
[47°09′30″S, 126°43′25″W] Reading: 1.13000 PPM
[47°09′32″S, 126°43′19″W] Reading: 1.64 Sv
[47°09′08″S, 126°43′53″W] Reading: 1.11 Sv
[47°09′16″S, 126°43′13″W] Reading: 1.06000 PPM
[47°09′16″S, 126°43′01″W] Raw reading: 0x632F53B1, offset +/-1
[47°09′34″S, 126°43′27″W] Reading: 0.36000 PPM
[47°09′30″S, 126°43′23″W] Reading: 1.13000 PPM
[47°09′13″S, 126°43′41″W] Reading: 0.02 Sv
now is a good time to sit back and read
[47°09′52″S, 126°43′30″W] Raw reading: 0x6320A902, offset +/-2
[47°09′01″S, 126°43′49″W] Raw reading: 0x63202A71, offset +/-2
[47°09′02″S, 126°43′52″W] Raw reading: 0x631EBCD1, offset +/-5
[47°09′21″S, 126°43′53″W] Raw reading: 0x631B8D31, offset +/-1
[47°09′26″S, 126°43′22″W] Raw reading: 0x631430D1, offset +/-4
[47°09′17″S, 126°43′06″W] Reading: 0.63000 PPM
[47°09′19″S, 126°43′25″W] Reading: 1.43 Sv
[47°09′17″S, 126°43′23″W] Reading: 0.75000 PPM
[47°09′55″S, 126°43′20″W] Reading: 1.23 Sv
[47°09′01″S, 126°43′23″W] Raw reading: 0x63110131, offset +/-1
[47°09′55″S, 126°43′01″W] Reading: 1.90 Sv
[47°09′48″S, 126°43′58″W] Reading: 0.95000 PPM
[47°09′13″S, 126°43′34″W] Reading: 0.42 Sv
[47°09′31″S, 126°43′32″W] Reading: 0.39000 PPM
[47°09′57″S, 126°43′34″W] Reading: 0.25000 PPM
[47°09′59″S, 126°43′16″W] Raw reading: 0x6308A7B1, offset +/-2
[47°09′51″S, 126°43′23″W] Raw reading: 0x63067531, offset +/-5
[47°09′37″S, 126°43′04″W] Reading: 0.41 Sv
[47°09′52″S, 126°43′16″W] Reading: 1.09000 PPM
[47°09′57″S, 126°43′17″W] Reading: 0.14000 PPM
[47°09′06″S, 126°43′46″W] Raw reading: 0x62FFB381, offset +/-5
[47°09′41″S, 126°43′01″W] Reading: 0.37000 PPM
(cont.)
Just to give some context on some of the components around the code structure.. I wrote this up around an earlier version of aggregate code. This generic bit simplifies things by removing the need of the Crud functions for each aggregate.
Domain ObjectsA domain object can be used as an aggregate by adding the event.AggregateRoot
struct and finish implementing event.Aggregate. The AggregateRoot implements logic for adding events after they are either Raised by a command or Appended by the eventstore Load or service ApplyFn methods. It also tracks the uncommitted events that are saved using the eventstore Save method.
type User struct {
Identity string ```json:"identity"`
CreatedAt time.Time
event.AggregateRoot
}
// StreamID for the aggregate when stored or loaded from ES.
func (a *User) StreamID() string {
return "user-" + a.Identity
}
// ApplyEvent to the aggregate state.
func (a *User) ApplyEvent(lis ...event.Event) {
for _, e := range lis {
switch e := e.(type) {
case *UserCreated:
a.Identity = e.Identity
a.CreatedAt = e.EventMeta().CreatedDate
/* ... */
}
}
}
Events
Events are applied to the aggregate. They are defined by adding the event.Meta
and implementing the getter/setters for event.Event
type UserCreated struct {
eventMeta event.Meta
Identity string
}
func (c *UserCreated) EventMeta() (m event.Meta) {
if c != nil {
m = c.eventMeta
}
return m
}
func (c *UserCreated) SetEventMeta(m event.Meta) {
if c != nil {
c.eventMeta = m
}
}
Reading Events from EventStore
With a domain object that implements the event.Aggregate
the event store client can load events and apply them using the Load(ctx, agg)
method.
// GetUser populates an user from event store.
func (rw *User) GetUser(ctx context.Context, userID string) (*domain.User, error) {
user := &domain.User{Identity: userID}
err := rw.es.Load(ctx, user)
if err != nil {
if err != nil {
if errors.Is(err, eventstore.ErrStreamNotFound) {
return user, ErrNotFound
}
return user, err
}
return nil, err
}
return user, err
}
OnX Commands
An OnX command will validate the state of the domain object can have the command performed on it. If it can be applied it raises the event using event.Raise() Otherwise it returns an error.
// OnCreate raises an UserCreated event to create the user.
// Note: The handler will check that the user does not already exsist.
func (a *User) OnCreate(identity string) error {
event.Raise(a, &UserCreated{Identity: identity})
return nil
}
// OnScored will attempt to score a task.
// If the task is not in a Created state it will fail.
func (a *Task) OnScored(taskID string, score int64, attributes Attributes) error {
if a.State != TaskStateCreated {
return fmt.Errorf("task expected created, got %s", a.State)
}
event.Raise(a, &TaskScored{TaskID: taskID, Attributes: attributes, Score: score})
return nil
}
Crud Operations for OnX Commands
The following functions in the aggregate service can be used to perform creation and updating of aggregates. The Update function will ensure the aggregate exists, where the Create is intended for non-existent aggregates. These can probably be combined into one function.
// Create is used when the stream does not yet exist.
func (rw *User) Create(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil && !errors.Is(err, ErrNotFound) {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
// Update is used when the stream already exists.
func (rw *User) Update(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
[47°09′49″S, 126°43′17″W] Reading: 0.84 Sv
[47°09′34″S, 126°43′12″W] Reading: 0.14000 PPM
[47°09′42″S, 126°43′03″W] Reading: 1.86 Sv
[47°09′51″S, 126°43′36″W] Reading: 1.22 Sv
[47°09′51″S, 126°43′52″W] Raw reading: 0x62F52781, offset +/-4
[47°09′48″S, 126°43′24″W] Raw reading: 0x62EF46D1, offset +/-3
[47°09′55″S, 126°43′08″W] Reading: 0.64 Sv
[47°09′57″S, 126°43′19″W] Reading: 0.79000 PPM
Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.
It’s super basic. Using tidwall/wal as the disk backing. The first use case I am playing with is an implementation of msgbus. I can post events to it and read them back in reverse order.
I plan to expand it to handle other event sourcing type things like aggregates and projections.
Find it here: sour-is/ev
@prologic@twtxt.net @movq@www.uninformativ.de @lyse@lyse.isobeef.org
[47°09′07″S, 126°43′31″W] Raw reading: 0x62EB5252, offset +/-2
[47°09′07″S, 126°43′17″W] Reading: 0.10000 PPM
[47°09′43″S, 126°43′06″W] Raw reading: 0x62E42E31, offset +/-5
[47°09′13″S, 126°43′15″W] Reading: 1.22 Sv
[47°09′50″S, 126°43′43″W] Reading: 1.40 Sv
[47°09′47″S, 126°43′40″W] Raw reading: 0x62DEE831, offset +/-5
shoutout to the woman that broke my heart so that I now read papers titled stuff like “Multiverse-wide Cooperation via Correlated Decision Making”
HOW DO THOSE PEOPLE READ SO MUCH
me reading planecrash is unstable bc while reading it I become so enthusiastic about reading textbooks that I go off and do that instead
[47°09′45″S, 126°43′30″W] Reading: 0.83000 PPM
[47°09′50″S, 126°43′44″W] Raw reading: 0x62DA2ED1, offset +/-2
[47°09′11″S, 126°43′43″W] Reading: 1.77 Sv
[47°09′58″S, 126°43′43″W] Reading: 1.69000 PPM
[47°09′49″S, 126°43′39″W] Reading: 0.24000 PPM
[47°09′42″S, 126°43′09″W] Reading: 0.11000 PPM
[47°09′13″S, 126°43′38″W] Raw reading: 0x62CEEC01, offset +/-3
[47°09′14″S, 126°43′47″W] Reading: 0.33000 PPM
[47°09′47″S, 126°43′06″W] Reading: 0.66 Sv