Skip to content
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

Presence heartbeat (v2) #3727

Merged
merged 5 commits into from
Mar 6, 2020
Merged

Conversation

rk-for-zulip
Copy link
Contributor

@rk-for-zulip rk-for-zulip commented Dec 17, 2019

Rewrite, and possibly unnecessarily thoroughly test, the presence-heartbeat code.

Resumption of PR #3704.

Fixes #3699.

@rk-for-zulip
Copy link
Contributor Author

The linter failure in the previous CI failure has been addressed by linter pragmata for now. (I thought I'd used that construct previously; but no, that was inside a .forEach.)

The current CI failure is due to a tests-only auxiliary file being (not unreasonably) mistaken for an actual test file by Jest, because it's in a subdirectory of the __tests__ folder that it's being used from. It clearly needs to be moved, but I see nowhere appropriate for it at present, and have no strong opinion about where it should go. A dev-src/ directory at the root, perhaps?

@rk-for-zulip
Copy link
Contributor Author

(Addendum: I haven't simply done that because, on reflection, the additional tests they're in support of may be more code than is worth having around. I'd prefer a second opinion on that.)

@chrisbobbe
Copy link
Contributor

chrisbobbe commented Jan 15, 2020

I'm not sure I can speak to the necessity of those tests, but maybe for the Jest failure -- it's a bit of a patch, but perhaps the code in src/presence/__tests__/aux/interval-set.js doesn't need to be in a separate file; it's not very long, it's only imported by one file, heartbeat-test.js, and that file includes similar helper code (the Lolex class) that hasn't been deemed large enough to separate.

Some brief research hasn't revealed any recommendations from Jest on where to put such things.

@rk-for-zulip
Copy link
Contributor Author

[...] and that file includes similar helper code (the Lolex class) that hasn't been deemed large enough to separate.

It probably should be, though: there are other timer-dependent tests (see: rg 'Date\.now' -g'**/__tests__/**') which could, and probably should, make use of Lolex.

They may also want to make use of interval trees – in which case I'll have to properly separate out... well, a lot of things – but even then the interval-tree library wrapper should probably be a sub-library, just for the sake of separation of concerns.

(If I just wanted to silence the Jest failure, I could put a dummy test in that file.)

@rk-for-zulip
Copy link
Contributor Author

Pinging @gnprice.

@gnprice
Copy link
Member

gnprice commented Feb 27, 2020

Marking priority, following the issue.

@gnprice
Copy link
Member

gnprice commented Feb 27, 2020

Thanks again @ray-kraesig for these changes! Now that the key parts of #3886 are in, want to rebase this to take advantage of Lolex being in master? (And the remaining commits of #3886 should be landing shortly, too, but I think those won't interact with this branch.)

I think the now-merged commits from #3886 also resolve the Jest file-layout questions discussed above. But please mention if I've missed something and that's not the case.

I'm also about to go read through this code -- I'll just skip over for now the bits that look like they were covered by #3886.

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

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

And read through the first half of this! Namely these commits, corresponding to what was in #3704:

37c5bb7 api [nfc]: rename reportPresence parameter
e12c452 presence: strip throttling from reportPresence wrapper
a32a1ac presence: add Heartbeat class (+ tests)
987a9b7 presence: move all presence-reporting logic into new HeartbeatComponent
f17b7d6 heartbeat: convert tests to use Lolex
e0a265d heartbeat: change presence-reporting protocol
3ca8fe1 heartbeat [nfc]: remove state parameter from callback

Thanks for the revisions! I like the changes. This looks close; there are a couple of things below.


I haven't yet read in detail the subsequent changes:

0d590a5 heartbeat [nfc]: add fine-grained event tracking to tests
5491e5e heartbeat [nfc]: extend heartbeat-test event system
d612271 deps: add interval-set library
27aa07c heartbeat: define additional constraints
0d14548 heartbeat tests: add app-suspension test cases

I think it might be simplest to reserve those for a subsequent PR.

One small commit-level remark: the summary line "heartbeat: define additional constraints" sounds to me like it's going to change something in the code's behavior. (There are a lot of things "constraints" could mean.) It turns out it doesn't, adding only a comment and some tests, and it'd be great to communicate that in the summary line. Perhaps s/define/Document, and test,/?

Comment on lines -23 to -25
const now = new Date();
if (differenceInSeconds(now, lastReportPresence) < 60) {
// TODO throttle properly; probably fold setInterval logic in here
Copy link
Member

Choose a reason for hiding this comment

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

Commit-level comments on this commit:
e12c452 presence: strip throttling from reportPresence wrapper

Nit: "would fix #3699 as literally reported" is no longer quite right, as we've edited #3699 so it describes the problem more broadly.

Related nit: "would fix #..." is a dangerous phrase to put in a commit message, because GitHub sees the "fix" and doesn't understand the "would". :-) Should basically always get reworded to hide from GitHub's auto-close rules, with e.g. a circumlocution like "would be a fix for #...".

Stepping back, though, I think my comment on this commit from the previous PR still applies:
#3704 (review)

This looks like a good implementation of what it says on the tin. I think it may not be part of what we want following today's discussion, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Stepping back, though, I think my comment on this commit from the previous PR still applies:
#3704 (review)

This looks like a good implementation of what it says on the tin. I think it may not be part of what we want following today's discussion, though.

The fix for this is basically to squash the second commit into the fourth commit. As there's not much substance to it, done. (Although this does make the commit message uncomfortably crowded. 😕)

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 result, thanks!

The commit message does get a bit longer. I think it might actually on net be simpler than before, though. One way it's certainly simpler is that the "If the user logs in and out" bullet previously had to tell a multi-step story ("This was previously concealed by ... which was itself removed in a previous patch") and that's now a simple before/after.

Comment on lines +37 to +42
AppState.addEventListener('change', this.updateHeartbeatState);
}

componentWillUnmount() {
AppState.removeEventListener('change', this.updateHeartbeatState);
Copy link
Member

Choose a reason for hiding this comment

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

We had a comment thread on this bit over on the previous PR: #3704 (comment)

Your explanation there makes sense to me! I'm convinced.

I also wanted to mention that thread here because you'd said:

(On the other hand, I'd argue that the lone existing use of .isActive, over in src/events/eventMiddleware.js, is a bug! Or at least is closely involved in one. But that's not directly relevant here; I'll elaborate somewhere else.)

and I don't want that thought to get lost. 🙂 Want to elaborate on that -- perhaps in a new issue, or a chat thread?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Eh, I might as well express my current thoughts on that in the form of commit messages.

Opened PR #3936.

Comment on lines 19 to 26
// Note that "PureComponent" is of questionable veracity: this component's
// entire purpose is to emit network calls for their observable side effects
// as a side effect of being rendered.
//
// However, it is at least true that there is never a need to call `render()`
// (and therefore `componentDidUpdate()`) if the props haven't changed.
class PresenceHeartbeat extends PureComponent<Props> {
Copy link
Member

@gnprice gnprice Feb 27, 2020

Choose a reason for hiding this comment

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

On the previous PR we discussed a previous version of this comment: #3704 (comment) Thanks for the updates!

I think it's pretty reasonable to say that the term "pure" here is confusing and of questionable veracity.

I think what my thought here boils down to now is that that complaint is really all about the naming of PureComponent. What we're doing is 100% consistent with the expectations of a React PureComponent, which are documented here -- it's just that those expectations don't include all the things that you might (reasonably!) expect the word "pure" to mean, or to be true of "a pure component".

A comment about that mismatch of PureComponent vs. the meaning of "pure" in general would be fine to have here. (Certainly it wouldn't be the first comment in our codebase that complains about a design choice that happened somewhere else 😉 -- I've written plenty of those.) What I don't want is a warning that we're hacking around or breaking some API expectation that potentially could expose us to problems when the React implementation changes, in a spot where in fact we aren't.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What I don't want is a warning that we're hacking around or breaking some API expectation that potentially could expose us to problems when the React implementation changes, in a spot where in fact we aren't.

Ah, I see the ambiguity now. Amended significantly.

Copy link
Member

Choose a reason for hiding this comment

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

New version looks great!

@rk-for-zulip
Copy link
Contributor Author

I think it might be simplest to reserve those for a subsequent PR.

Fair enough! Snipped for now.

@gnprice
Copy link
Member

gnprice commented Mar 4, 2020

Thanks @ray-kraesig for the revisions!

One question and a nit, both in the commit messages.

Fixes (the original version of) #3699, and is work towards #3659.

Seems to me like it actually fixes #3699. Is there a particular aspect of that that's still open that you have in mind?

Maybe this is a sign that my revised title was too vague 🙂 : "Presence-reporting logic is confused, in overlapping ways". I think the bar for closing the issue isn't that we become sure there are no bugs or confusions in the new version -- just that it's no longer confused in the ways we knew about. And of course a related good thing to achieve (as this PR does) is that it's written in a way that we can hope is less prone to confusion. 😉

heartbeat: change presence-reporting protocol

Following discussion on chat.zulip.org's #mobile-team channel, adjust

s/channel/stream/ :smile:

Oh, and I guess a second nit: s/heartbeat/presence/ in that summary line would feel a bit more on-point to me. I think it's because this change is mainly aimed at the server-facing reporting of presence, rather than the internal implementation choices (even though it involves making changes to the latter to accomplish its changes to the former.)

@rk-for-zulip
Copy link
Contributor Author

Seems to me like it actually fixes #3699. Is there a particular aspect of that that's still open that you have in mind?

As of that commit, yes: idle messages are still sent, under the (existing) confusion that they should be. The following commit should have the relevant fix-note. (Done.)

@gnprice
Copy link
Member

gnprice commented Mar 6, 2020

As of that commit, yes: idle messages are still sent, under the (existing) confusion that they should be. The following commit should have the relevant fix-note. (Done.)

Ah, got it. Thanks!

I'll merge shortly. I think I'll make one small further edit to the commit messages: on this "Fixes (the original version of) #3699" commit, I'll add a mention that a fix to the remainder of that issue is coming up next. Reduces the suspense a bit for the reader. 😉

Mobile apps are not usually described as "having focus", and the
underlying parameter value is `"active"` anyway.
Add `Heartbeat`, a generic-looking class which will be used to perform
presence-reporting.

Also add tests, to ensure that it behaves as expected.
Replace the existing presence-reporting logic with HeartbeatComponent,
instantiated as part of a new zero-display React component located
under AppEventHandlers. In particular, strip the throttling logic from
`reportPresence`.

This fixes at least three issues:

* If the user logged in and out, or otherwise performed an additional
  "initial fetch" without killing the app, the presence timer would be
  started multiple times. (The negative effects of this were
  previously concealed by the throttling logic.)

* `idle` events were not treated differently from `active` events by
  the throttling logic, and would almost invariably be swallowed.

* If the user went idle (e.g. by hitting the Home button), the
  presence timer would still run, and would still attempt to send
  `active` notifications. (These attempts were often suppressed while
  the app was in the background, but this was never guaranteed.)

Fixes the original version of zulip#3699; the next commit will complete the
fix for that issue.

This is also work towards zulip#3659.  Unfortunately, there is no
user-visible change yet: modifications will be needed on the Zulip
server side as well.
Following discussion on chat.zulip.org's #mobile-team stream [0], adjust
the presence-reporting protocol so that it never sends "idle".

Fixes zulip#3699.

[0] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/presence.20and.20notifications/near/800081
The heartbeat callback is now only ever called with `true`. Simplify
the associated code by removing the now-constant argument.
@gnprice gnprice merged commit 9e3d52d into zulip:master Mar 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a-presence/status Presence, and "away status" P1 high-priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Presence-reporting logic is confused, in overlapping ways
3 participants