Skip to content

Refactor activity log types #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 15, 2022

Conversation

juliangruber
Copy link
Member

To help clarify my reasoning in #110 (comment), I decided to code it up.

@bajtos what do you think?

@juliangruber juliangruber requested a review from bajtos September 14, 2022 15:29
@juliangruber juliangruber marked this pull request as draft September 14, 2022 16:10
@juliangruber
Copy link
Member Author

The types are incomplete, will fix

@juliangruber juliangruber marked this pull request as ready for review September 14, 2022 16:17
@juliangruber juliangruber changed the title prototype activity log layout prototype activity log types Sep 14, 2022
@@ -1,31 +1,30 @@
'use strict'

/** @typedef {import('./typings').ActivityEvent} ActivityEvent */
/** @typedef {import('./typings').ActivityEntry} ActivityEntry */
/** @typedef {import('./typings').RecordActivityOptions} RecordActivityOptions */
Copy link
Member Author

@juliangruber juliangruber Sep 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simplify the types by thinking about them this way: One is the actual activity (Activity), the other is the options passed to .recordActivity (RecordActivityOptions). To me this is clearer than having to juggle Activity, Event and Entry in your mind, and the variable names are nicely consistent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also rename this to use Event instead of Activity, which is the more common term, but I do think here Activity works better, because it's clearer that we're referring to a product concept, and not a generic event.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea to user Activity to describe the data structure we are storing in our log 👍🏻

I have mixed feelings about the name RecordActivityOptions. In my mind, options is for optional configuration tweaking the behaviour of a function. How about something like RecordActivityFields or NewActivityProps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I tried to look up prior art on this and found that the types for node for example call arguments options but their type ...Args:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/3b2cb0f873d4caca3e4fc01aeaf9f8ec40852d8f/types/node/http.d.ts#L138-L139

Either would work for me 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use NewActivityArgs then.

export interface RecordActivityOptions {
type: ActivityType;
source: ActivitySource;
message: string;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the inheritance, because Activity is the main type, and RecordActivityOptions more a partial of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to remove inheritance 👍🏻

An alternative approach that does not duplicate property definitions:

export type RecordActivityOptions = Pick<Activity, 'type' | 'source' | 'message'>;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool I like that!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the second thought, this is probably more future proof when it comes to adding new Activity props:

export type RecordActivityOptions = Omit<Activity, 'id' | 'timestamp'>;

@juliangruber juliangruber changed the title prototype activity log types Refactor activity log types Sep 14, 2022
return a.timestamp !== b.timestamp
? b.timestamp - a.timestamp
: a.id.localeCompare(b.id)
})
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we moved sorting to the backend, then we wouldn't have to sort on every new event. Otherwise I think we could do it in either location, and having it on the view layer might be easier to get right wrt race conditions

@juliangruber
Copy link
Member Author

Error: main/test/activity-log.test.js(55,5): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
Error: Process completed with exit code 2.

@bajtos I don't understand why this isn't showing up in main ci 🤔

/** @type {ActivityEntry} */
const entry = {
id: String(nextId),
recordActivity ({ source, type, message }) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a method of ActivityLog class, maybe we can shorten the name to simply record?

Suggested change
recordActivity ({ source, type, message }) {
record ({ source, type, message }) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea!

export interface RecordActivityOptions {
type: ActivityType;
source: ActivitySource;
message: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to remove inheritance 👍🏻

An alternative approach that does not duplicate property definitions:

export type RecordActivityOptions = Pick<Activity, 'type' | 'source' | 'message'>;

activities.sort((a, b) => {
return a.timestamp !== b.timestamp
? b.timestamp - a.timestamp
: a.id.localeCompare(b.id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure ids created by crypto.randomUUID() monotonically increasing over time?

Maybe the order of events reported in the same millisecond does not matter and that's why we can fallback our sorting comparisons to comparing ids?

I think it would be great to add a code comment to clarify this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't increase, and that's not an issue in my mind. As long as we sort deterministically, no human will have noticed that the events from the same millisecond have been mixed up. And we sort deterministically by giving events IDs when storing them. I'll add a comment 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative idea: Is there a timing api with such precision that it's guaranteed that two successive calls won't get the same values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.hrtime() seems to work:

> [process.hrtime(), process.hrtime()]
[ [ 845635, 876114697 ], [ 845635, 876117208 ] ]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we sort deterministically, no human will have noticed that the events from the same millisecond have been mixed up.

Unless these two events happen to be something like "Starting the station" and "Starting Saturn module". It would look weird to have them in the reverse direction.

Anyhow, I think this does not really matter, especially not now.

@bajtos
Copy link
Member

bajtos commented Sep 15, 2022

Let's merge this into my branch and then work together there.

@bajtos bajtos merged commit d8e010f into feat-activity-log-backend Sep 15, 2022
@bajtos bajtos deleted the prototype-activity-log-julian branch September 15, 2022 11:18
@bajtos bajtos mentioned this pull request May 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants