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 engine - states and events #178

Merged
merged 16 commits into from
Feb 8, 2024
Merged

Presence engine - states and events #178

merged 16 commits into from
Feb 8, 2024

Conversation

seba-aln
Copy link
Contributor

@seba-aln seba-aln commented Jan 3, 2024

feat: Optional Event Engine for Subscribe Loop

Copy link
Contributor

@Xavrax Xavrax left a comment

Choose a reason for hiding this comment

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

LGTB 2 comments

pubnub/event_engine/manage_effects.py Outdated Show resolved Hide resolved
pubnub/pubnub_asyncio.py Show resolved Hide resolved
@seba-aln seba-aln force-pushed the event-engine/presence branch 2 times, most recently from 54b367b to 3ce8c86 Compare February 5, 2024 21:08
def dispatch_effect(self, effect: effects.PNEffect):
if not self._managed_effects_factory:
self._managed_effects_factory = manage_effects.ManagedEffectFactory(self._pubnub, self._event_engine)
def dispatch_effect(self, invocation: invocations.PNInvocation):
Copy link

@jguz-pubnub jguz-pubnub Feb 6, 2024

Choose a reason for hiding this comment

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

Mhm, I don't think we should differentiate Effects that emit status (or anything else) from other non-cancellable Effects. They should have the same interface or method to fire them. I know it's not possible to fix it, so let's keep this comment so that others are aware of this difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will look at it

if self.invocation.region:
request.region(self.invocation.region)

if feature_enabled('PN_MAINTAIN_PRESENCE_STATE') and hasattr(self.pubnub, 'state_container'):
Copy link

@jguz-pubnub jguz-pubnub Feb 6, 2024

Choose a reason for hiding this comment

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

I'm curious, why do you need a check like this?

hasattr(self.pubnub, 'state_container')

Isn't the internal state_container variable always present in PubNub?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no it's not the internal variable for PubNub. It's only in PubNubAsyncio because it's only used there. This may be redundant check. I'll verify it

class HeartbeatDelayedEffect(Effect):
def __init__(self, pubnub_instance, event_engine_instance,
invocation: Union[invocations.PNManageableInvocation, invocations.PNCancelInvocation]) -> None:
super().__init__(pubnub_instance, event_engine_instance, invocation)

Choose a reason for hiding this comment

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

The same, let's think about improving it while working on a new reconnection policy

return self._managed_invocations[invocation.__class__.__name__](self._pubnub, self._event_engine, invocation)


class EmitEffect:

Choose a reason for hiding this comment

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

There should be two separate Effects for emitting messages and status, respectively. Let's leave it if this requires significant amount of work

Copy link
Contributor

@Xavrax Xavrax left a comment

Choose a reason for hiding this comment

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

LGTM - some nitpickings and questions

self.channel_states[channel] = state

def get_state(self, channels: list):
return {**self.get_channels_states(channels)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Q: What exactly meant double wyłuskanie here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's a dict extraction. Also it's redundant. I'll fix it :)

Comment on lines +20 to +25
pubnub: PubNub = None
event_engine = None
invocation: Union[invocations.PNManageableInvocation, invocations.PNCancelInvocation]
stop_event = None
logger: logging.Logger
task: asyncio.Task
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpicking:
It is easy to make a mistake when looking at the default values.

Suggested change
pubnub: PubNub = None
event_engine = None
invocation: Union[invocations.PNManageableInvocation, invocations.PNCancelInvocation]
stop_event = None
logger: logging.Logger
task: asyncio.Task
pubnub: PubNub = None
event_engine: StateMachine = None
stop_event: PNEvent = None
invocation: Union[invocations.PNManageableInvocation, invocations.PNCancelInvocation]
logger: logging.Logger
task: asyncio.Task

Comment on lines +73 to +88
tt = self.invocation.timetoken or 0
if hasattr(self.pubnub, 'event_loop'):
self.stop_event = self.get_new_stop_event()
self.run_async(self.handshake_async(channels=channels,
groups=groups,
timetoken=tt,
stop_event=self.stop_event))

async def handshake_async(self, channels, groups, stop_event, timetoken: int = 0):
request = Subscribe(self.pubnub).channels(channels).channel_groups(groups).cancellation_event(stop_event)

if feature_enabled('PN_MAINTAIN_PRESENCE_STATE') and hasattr(self.pubnub, 'state_container'):
state_container = self.pubnub.state_container
request.state(state_container.get_state(channels))

request.timetoken(0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need to set the timetoken to zero in each of the occurrences? It would be nice to have a single point of truth in case of code-reviewing-and-debugging.
In C# there was so many of the reassignments like that and it caused the bug. :/

Comment on lines +165 to +171
def give_up(self, reason: PubNubException, attempt: int, timetoken: int = 0):
self.logger.error(f"GiveUp called on Unspecific event. Reason: {reason}, Attempt: {attempt} TT:{timetoken}")
raise PubNubException('Unspecified Invocation')

def failure(self, reason: PubNubException, attempt: int, timetoken: int = 0):
self.logger.error(f"Failure called on Unspecific event. Reason: {reason}, Attempt: {attempt} TT:{timetoken}")
raise PubNubException('Unspecified Invocation')
Copy link
Contributor

Choose a reason for hiding this comment

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

same as above?

Comment on lines +317 to +318
except asyncio.CancelledError:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

It only catches the CancelledError. Is it intended?


def on_exit(self):
super().on_exit()
return effects.CancelHandshakeReconnectEffect()
return invocations.CancelHandshakeReconnectInvocation()

def disconnect(self, event: events.DisconnectEvent, context: PNContext) -> PNTransition:

Choose a reason for hiding this comment

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

IMHO all transitions should be part of Transition function that was mentioned in the State Machine Architecture decision. This shouldn't be the logic of any State. Let's keep this comment so that others are aware of this difference.

@jguz-pubnub jguz-pubnub self-requested a review February 6, 2024 11:48
Copy link

@jguz-pubnub jguz-pubnub left a comment

Choose a reason for hiding this comment

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

Approved. However, some areas might differ in terms of decisions/design in comparison to other SDKs. Let's skip it for now if Presence & Subscribe works as expected and let's think about any adjustments/improvements in the future.

print(f"{presence.event} {presence.uuid}\n")

def status(self, pubnub, status):
# print(status.__dict__)
Copy link
Contributor

Choose a reason for hiding this comment

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

comment?

@@ -19,7 +19,7 @@ def string(cls, method):
return "PATCH"


class PNStatusCategory(object):
class PNStatusCategory(Enum):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Disclaimer: For most of situations this doesn't change anything. Enum inherits from object (which is leftover from py2.7 btw) and only adds functionality like this status.category.name to extract the name based on the value

@seba-aln
Copy link
Contributor Author

seba-aln commented Feb 8, 2024

@pubnub-release-bot release

@seba-aln seba-aln merged commit 4c03ecd into master Feb 8, 2024
9 checks passed
@seba-aln seba-aln deleted the event-engine/presence branch February 8, 2024 11:49
@pubnub-release-bot
Copy link
Contributor

🚀 Release successfully completed 🚀

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.

4 participants