Skip to content

feat(Push): add Appwrite Push (MQTT 5) adapter#129

Open
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:fix/122-appwrite-push-mqtt5
Open

feat(Push): add Appwrite Push (MQTT 5) adapter#129
deepshekhardas wants to merge 1 commit into
utopia-php:mainfrom
deepshekhardas:fix/122-appwrite-push-mqtt5

Conversation

@deepshekhardas

Copy link
Copy Markdown

Port of PR #122 by abnegate.

Adds Appwrite Push - a self-hosted, low-power alternative to FCM/APNS that publishes notifications over MQTT 5 to per-device topics.

Changes:

  • New MQTT 5 control-packet codec (Helpers/MQTT) - pure PHP, no extra dependency
  • New Appwrite Push adapter for MQTT 5 publishing
  • Fake broker for integration testing
  • Unit and integration tests

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds the Appwrite Push adapter \u2014 a pure-PHP MQTT 5 publisher that fans out push notifications to per-device topics over a pipelined QoS-1 session. It includes the full MQTT 5 codec, the adapter, a Swoole-based fake broker, and unit/integration test suites.

  • The codec and pipelining logic are well-structured; pipelinedPublish correctly bounds in-flight messages to the broker-advertised receiveMaximum.
  • Several state-management issues from prior review rounds remain unaddressed: readBuffer not reset between connections, rtrim instead of trim on the endpoint, receiveMaximum permanently narrowed across process() calls, and the readProperties default branch silently dropping all subsequent properties on any unknown identifier.

Confidence Score: 3/5

Not yet safe to merge — multiple defects in the MQTT codec and adapter state management from previous reviews remain unfixed.

Four issues identified in prior review rounds are still present: readBuffer not cleared between connections, rtrim instead of trim on the endpoint, receiveMaximum narrowed permanently across calls, and the readProperties default branch returning early on any unknown property identifier — silently discarding all subsequent properties including receiveMaximum if a real broker sends an unhandled property first.

src/Utopia/Messaging/Helpers/MQTT.php (readProperties early-return) and src/Utopia/Messaging/Adapter/Push/Appwrite.php (readBuffer, rtrim, receiveMaximum state) need attention before merge.

Important Files Changed

Filename Overview
src/Utopia/Messaging/Helpers/MQTT.php New MQTT 5 codec; readProperties default branch silently returns early on any unknown property identifier, breaking parsing of all subsequent properties in that packet.
src/Utopia/Messaging/Adapter/Push/Appwrite.php New MQTT 5 publisher adapter; carries three unfixed state bugs from prior reviews: readBuffer not cleared, rtrim instead of trim, and receiveMaximum permanently narrowed.
tests/Messaging/Adapter/Push/FakeBroker.php Swoole-based fake broker; silences stdout/stderr making crash diagnosis difficult.
tests/Messaging/Adapter/Push/AppwriteTest.php Integration tests cover happy-path, pipelined burst, and rejected-token scenarios; broker stderr silenced via /dev/null.
tests/Messaging/Helpers/MQTTTest.php Unit tests covering encode/decode round-trips, partial-buffer handling, and validation edge cases; correct.
Dockerfile Adds Swoole build step for FakeBroker; build-dep cleanup is correct.

Reviews (3): Last reviewed commit: "feat(Push): add Appwrite Push (MQTT 5) a..." | Re-trigger Greptile

}

public function getMaxMessagesPerRequest(): int
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 readBuffer not cleared between process() calls

$this->readBuffer is never reset at the start of each connection. If the adapter instance is reused (e.g., send() is called twice), or if the broker sends an extra packet after the last PUBACK (e.g., a PINGREQ that landed in the buffer just before disconnect), that residual data persists into the next call. On the next invocation readPacket() would immediately return the leftover packet as if it were the new connection's CONNACK, causing handshake() to throw "Broker did not respond with CONNACK" even on a healthy connection.

Add $this->readBuffer = ''; at the start of connect() or at the top of process() to isolate each connection's read state.


private function resolveEndpoint(): string
{
$endpoint = \rtrim($this->endpoint);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 rtrim strips only trailing whitespace, so a leading space in the configured endpoint (e.g., " broker.example.com") would produce a malformed URL like tls:// broker.example.com:8883 that stream_socket_client rejects. Use trim to strip both ends.

Suggested change
$endpoint = \rtrim($this->endpoint);
$endpoint = \trim($this->endpoint);

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +381 to +384
$packet = MQTT::decodePacket($this->readBuffer);
if ($packet !== null) {
return $packet;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 receiveMaximum decreases monotonically across process() calls

$this->receiveMaximum is instance state that is only ever updated via min() in handshake(). If the adapter is reused across multiple send() calls and the broker advertises a low receiveMaximum (say 10) on the first call, subsequent connections — even to a different broker endpoint — will be throttled to that minimum permanently for the lifetime of the object. Resetting it to the class-default (or to 65535) at the start of each connect() would make each connection's window independent.

@deepshekhardas deepshekhardas force-pushed the fix/122-appwrite-push-mqtt5 branch from 7e1b5cd to 2dc61de Compare June 17, 2026 09:14
Based on PR utopia-php#122 by abnegate. Adds Appwrite Push - a self-hosted MQTT 5 based push notification adapter with minimal MQTT 5 control-packet codec.
@deepshekhardas deepshekhardas force-pushed the fix/122-appwrite-push-mqtt5 branch from 2dc61de to 00b90df Compare July 10, 2026 05:01
@deepshekhardas

Copy link
Copy Markdown
Author

Following up - this PR has been open for 1 month. Let me know if any changes are needed or if the implementation approach needs adjustment.

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.

1 participant