Skip to content

Commit

Permalink
Feature/add benchmark tables (#16)
Browse files Browse the repository at this point in the history
* Add benchmarks

* Update table formatting in README.md for improved readability

* Consolidate Flutter and Dart versions in README.md for better clarity

* Add benchmark information

* Add benchmark description

* Update example

* Add custom configuration for Spinify client in README.md and example

* Add subscription example and update README for clarity

* Clarify JSON codec support limitation in README

* Update README.md

* Update README.md

* Update package info
  • Loading branch information
PlugFox authored Nov 28, 2024
1 parent 131bc61 commit e5df3cf
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.0

- Add benchmark information to README

## 0.1.0-pre.2

- Support of custom HTTP client for WebSocket VM connection
Expand Down
138 changes: 131 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,144 @@
[![Linter](https://img.shields.io/badge/style-linter-40c4ff.svg)](https://pub.dev/packages/linter)
[![GitHub stars](https://img.shields.io/github/stars/plugfox/spinify?style=social)](https://github.com/plugfox/spinify/)

Websocket client for [Centrifugo server](https://github.com/centrifugal/centrifugo) and [Centrifuge library](https://github.com/centrifugal/centrifuge).
Spinify is a Dart and Flutter library that provides an efficient client implementation for [Centrifugo](https://centrifugal.dev/), a scalable real-time messaging server.
This library allows you to connect your Dart or Flutter applications to [Centrifugo server](https://github.com/centrifugal/centrifugo) and [Centrifuge library](https://github.com/centrifugal/centrifuge), enabling real-time updates, presence information, history fetching, and more.

## Features

- **Connection Management**: Establish, monitor, and close connections to Centrifugo servers.
- **Subscriptions**: Create, manage, and remove client-side and server-side subscriptions.
- **Event Streaming**: Stream channel events for real-time updates.
- **Data Publishing**: Publish messages to specific channels.
- **Asynchronous Messaging**: Send custom asynchronous messages to the server.
- **Presence Management**: Retrieve presence and presence statistics for channels.
- **History Retrieval**: Fetch publication history for specific channels.
- **Remote Procedure Calls (RPC)**: Perform server-side method invocations.
- **Metrics**: Access metrics for client performance and statistics.
- **Reconnecting**: Automatically reconnect to the server in case of a connection failure.
- **Protobuf Transport**: Use Protobuf codec for data serialization.
- **Custom Configuration**: Configure client settings, timeouts, and transport options.
- **Error Handling**: Handle errors and exceptions gracefully.
- **Logging**: Log events, errors, and messages for debugging purposes.
- **Cross-Platform**: Run on Dart VM, Flutter, and Web platforms.
- **Performance**: Achieve high throughput and low latency for real-time messaging.

## Installation

Add the following dependency to your `pubspec.yaml` file:
Add the following dependency to your `pubspec.yaml` file and specify the version:

```yaml
dependencies:
spinify: <version>
spinify: ^X.Y.Z
```
Then fetch the package using:
```bash
flutter pub get
```

## Examples

Simple usage of the library:

```dart
final client = Spinify();
await client.connect(url);
// ...
await client.close();
```

Add custom configuration:

```dart
final httpClient = io.HttpClient(
context: io.SecurityContext(
withTrustedRoots: true,
)..setTrustedCertificatesBytes([/* bytes array */]),
);
final client = Spinify(
config: SpinifyConfig(
client: (name: 'app', version: '1.0.0'),
timeout: const Duration(seconds: 15),
serverPingDelay: const Duration(seconds: 8),
connectionRetryInterval: (
min: const Duration(milliseconds: 250),
max: const Duration(seconds: 15),
),
getToken: () async => '<token>',
getPayload: () async => utf8.encode('Hello, World!'),
codec: SpinifyProtobufCodec(),
transportBuilder: SpinifyTransportAdapter.vm(
compression: io.CompressionOptions.compressionDefault,
customClient: httpClient,
userAgent: 'Dart',
),
logger: (level, event, message, context) => print('[$event] $message'),
),
);
```

Subscribe to a channel:

```dart
final sub = client.newSubscription('notifications:index');
sub.stream.publication().map((p) => utf8.decode(p.data)).listen(print);
await sub.subscribe();
await sub.publish(utf8.encode('Hello, World!'));
await sub.unsubscribe();
```

## Benchmarks

This benchmark measures the performance of the [spinify](https://pub.dev/packages/spinify) and [centrifuge-dart](https://pub.dev/packages/centrifuge) libraries by sending and receiving a series of messages to a Centrifugo server and tracking key performance metrics such as throughput and latency.

Environment:

```
Windows 11 Pro 64-bit
CPU 13th Gen Intel Core i7-13700K
Chrome Version 131.0.6778.86 (Official Build) (64-bit)
Docker version 27.1.1
Docker image centrifugo/centrifugo:v5
Flutter 3.24.5 • Dart 3.5.4
Package spinify v0.1.0
Package centrifuge-dart v0.14.1
```

The benchmark sends 10,000 messages of a certain size one after the other and measure the time.
Each message is sent sequentially: the client waits for the server's response before sending the next message.

### Windows (Dart VM)

| | Spinify | Centrifuge-Dart |
| ----- | ------------------- | ------------------- |
| 1 KB | 5763 msg/s (7MB/s) | 5361 msg/s (6MB/s) |
| 5 KB | 4405 msg/s (22MB/s) | 3731 msg/s (18MB/s) |
| 10 KB | 3717 msg/s (37MB/s) | 2857 msg/s (28MB/s) |
| 14 KB | 3305 msg/s (45MB/s) | 2564 msg/s (35MB/s) |
| 16 KB | 3091 msg/s (50MB/s) | 1982 msg/s (32MB/s) |
| 20 KB | 2812 msg/s (56MB/s) | 1811 msg/s (36MB/s) |
| 30 KB | 2463 msg/s (72MB/s) | 1470 msg/s (43MB/s) |
| 40 KB | 1937 msg/s (76MB/s) | 1089 msg/s (42MB/s) |
| 50 KB | 1740 msg/s (85MB/s) | 967 msg/s (47MB/s) |
| 60 KB | 1583 msg/s (92MB/s) | 877 msg/s (51MB/s) |

_\* Messages larger than 64 KB are not supported._

### Browser (WASM and JS)

| | Spinify WASM | Spinify JS | Centrifuge-Dart JS |
| ----- | ------------------- | ------------------- | ------------------- |
| 1 KB | 3676 msg/s (4MB/s) | 3502 msg/s (4MB/s) | 3067 msg/s (3MB/s) |
| 5 KB | 2659 msg/s (13MB/s) | 3484 msg/s (17MB/s) | 2207 msg/s (11MB/s) |
| 10 KB | 1926 msg/s (19MB/s) | 3189 msg/s (31MB/s) | 1584 msg/s (15MB/s) |
| 14 KB | 1670 msg/s (22MB/s) | 2890 msg/s (39MB/s) | 1287 msg/s (17MB/s) |
| 16 KB | 39 msg/s (662KB/s) | 39 msg/s (662KB/s) | 39 msg/s (662KB/s) |

_\* After message sizes exceed 15 KB, there is a noticeable performance drop._

## Features and Roadmap

- ✅ Connect to a server
Expand Down Expand Up @@ -54,8 +181,7 @@ dependencies:
- ✅ Performance comparison with other libraries
- ✅ WASM compatibility
- ❌ 95% test coverage
- ❌ JSON codec support
- ❌ Flutter package
- ❌ JSON codec support for transport
- ❌ DevTools extension
- ❌ Run in separate isolate
- ❌ Middleware support
Expand All @@ -64,8 +190,6 @@ dependencies:
- ❌ Optimistic subscriptions
- ❌ Delta compression

## Example
## More resources

- [Library documentation](https://pub.dev/documentation/spinify/latest/)
Expand Down
30 changes: 29 additions & 1 deletion example/benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,36 @@

## Build

JS

```bash
flutter build web --release --no-source-maps --pwa-strategy offline-first --web-resources-cdn --base-href / --web-renderer canvaskit
```

WASM

```bash
flutter build web --release --no-source-maps --pwa-strategy offline-first --web-resources-cdn --base-href / --wasm
```

## Serve

Windows

```bash
flutter run -d windows --release
```

JS

```bash
flutter run -d chrome --release --web-resources-cdn --web-renderer canvaskit
```

WASM

```bash
flutter build web --release --no-source-maps --pwa-strategy offline-first --web-renderer canvaskit --web-resources-cdn --base-href /
flutter run -d chrome --release --web-resources-cdn --wasm
```

## Deploy
Expand Down
33 changes: 30 additions & 3 deletions example/echo/main.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1,54 @@
// ignore_for_file: avoid_print
// ignore_for_file: avoid_print, implicit_call_tearoffs

import 'dart:async';
import 'dart:io' as io;

import 'package:spinify/spinify.dart';

void main(List<String> args) {
void main(List<String> args) async {
var url = args.firstWhere((a) => a.startsWith('--url='), orElse: () => '');
if (url.isNotEmpty) url = url.substring(6).trim();
if (url.isEmpty) url = io.Platform.environment['URL'] ?? '';
if (url.isEmpty) url = const String.fromEnvironment('URL', defaultValue: '');
if (url.isEmpty) url = 'ws://localhost:8000/connection/websocket';

final httpClient = io.HttpClient(
context: io.SecurityContext(
withTrustedRoots: true,
), //..setTrustedCertificatesBytes([/* bytes array */])
);

final client = Spinify(
config: SpinifyConfig(
client: (name: 'app', version: '1.0.0'),
timeout: const Duration(seconds: 15),
serverPingDelay: const Duration(seconds: 8),
connectionRetryInterval: (
min: const Duration(milliseconds: 250),
max: const Duration(seconds: 15),
),
/* getToken: () async => '<token>', */
/* getPayload: () async => utf8.encode('Hello, World!'), */
codec: SpinifyProtobufCodec(),
transportBuilder: SpinifyTransportAdapter.vm(
compression: io.CompressionOptions.compressionDefault,
customClient: httpClient,
userAgent: 'Dart',
),
logger: (level, event, message, context) => print('[$event] $message'),
),
);

Timer(const Duration(minutes: 1), () async {
await client.close();
io.exit(0);
});

var prev = client.state;
client.states.listen((next) {
print('$prev -> $next');
prev = next;
});

client.connect(url).ignore();
await client.connect(url);
}
18 changes: 9 additions & 9 deletions lib/src/model/pubspec.yaml.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ sealed class Pubspec {
static const PubspecVersion version = (
/// Non-canonical string representation of the version as provided
/// in the pubspec.yaml file.
representation: r'0.1.0-pre.2',
representation: r'0.1.0',

/// Returns a 'canonicalized' representation
/// of the application version.
/// This represents the version string in accordance with
/// Semantic Versioning (SemVer) standards.
canonical: r'0.1.0-pre.2',
canonical: r'0.1.0',

/// MAJOR version when you make incompatible API changes.
/// The major version number: 1 in "1.2.3".
Expand All @@ -115,7 +115,7 @@ sealed class Pubspec {
patch: 0,

/// The pre-release identifier: "foo" in "1.2.3-foo".
preRelease: <String>[r'pre', r'2'],
preRelease: <String>[],

/// The build identifier: "foo" in "1.2.3+foo".
build: <String>[],
Expand All @@ -125,12 +125,12 @@ sealed class Pubspec {
static final DateTime timestamp = DateTime.utc(
2024,
11,
21,
14,
36,
47,
322,
307,
28,
18,
51,
31,
283,
52,
);

/// Name
Expand Down
4 changes: 2 additions & 2 deletions lib/src/spinify.dart
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ final class Spinify implements ISpinify {
if (_refreshTimer != null) {
warning('Health check failed: refresh timer set during connect');
}
case SpinifyState$Connected _:
if (_refreshTimer == null) {
case SpinifyState$Connected state:
if (state.expires && _refreshTimer == null) {
warning('Health check failed: no refresh timer set');
_setUpRefreshConnection();
}
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: >
Dart client to communicate with Centrifuge and Centrifugo from Dart and Flutter
over WebSockets with Protobuf support.
version: 0.1.0-pre.2
version: 0.1.0

homepage: https://centrifugal.dev

Expand Down

0 comments on commit e5df3cf

Please sign in to comment.