Skip to content

Commit e5df3cf

Browse files
authored
Feature/add benchmark tables (#16)
* 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
1 parent 131bc61 commit e5df3cf

File tree

7 files changed

+206
-23
lines changed

7 files changed

+206
-23
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.1.0
2+
3+
- Add benchmark information to README
4+
15
## 0.1.0-pre.2
26

37
- Support of custom HTTP client for WebSocket VM connection

README.md

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,144 @@
77
[![Linter](https://img.shields.io/badge/style-linter-40c4ff.svg)](https://pub.dev/packages/linter)
88
[![GitHub stars](https://img.shields.io/github/stars/plugfox/spinify?style=social)](https://github.com/plugfox/spinify/)
99

10-
Websocket client for [Centrifugo server](https://github.com/centrifugal/centrifugo) and [Centrifuge library](https://github.com/centrifugal/centrifuge).
10+
Spinify is a Dart and Flutter library that provides an efficient client implementation for [Centrifugo](https://centrifugal.dev/), a scalable real-time messaging server.
11+
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.
12+
13+
## Features
14+
15+
- **Connection Management**: Establish, monitor, and close connections to Centrifugo servers.
16+
- **Subscriptions**: Create, manage, and remove client-side and server-side subscriptions.
17+
- **Event Streaming**: Stream channel events for real-time updates.
18+
- **Data Publishing**: Publish messages to specific channels.
19+
- **Asynchronous Messaging**: Send custom asynchronous messages to the server.
20+
- **Presence Management**: Retrieve presence and presence statistics for channels.
21+
- **History Retrieval**: Fetch publication history for specific channels.
22+
- **Remote Procedure Calls (RPC)**: Perform server-side method invocations.
23+
- **Metrics**: Access metrics for client performance and statistics.
24+
- **Reconnecting**: Automatically reconnect to the server in case of a connection failure.
25+
- **Protobuf Transport**: Use Protobuf codec for data serialization.
26+
- **Custom Configuration**: Configure client settings, timeouts, and transport options.
27+
- **Error Handling**: Handle errors and exceptions gracefully.
28+
- **Logging**: Log events, errors, and messages for debugging purposes.
29+
- **Cross-Platform**: Run on Dart VM, Flutter, and Web platforms.
30+
- **Performance**: Achieve high throughput and low latency for real-time messaging.
1131

1232
## Installation
1333

14-
Add the following dependency to your `pubspec.yaml` file:
34+
Add the following dependency to your `pubspec.yaml` file and specify the version:
1535

1636
```yaml
1737
dependencies:
18-
spinify: <version>
38+
spinify: ^X.Y.Z
1939
```
2040
41+
Then fetch the package using:
42+
43+
```bash
44+
flutter pub get
45+
```
46+
47+
## Examples
48+
49+
Simple usage of the library:
50+
51+
```dart
52+
final client = Spinify();
53+
await client.connect(url);
54+
// ...
55+
await client.close();
56+
```
57+
58+
Add custom configuration:
59+
60+
```dart
61+
final httpClient = io.HttpClient(
62+
context: io.SecurityContext(
63+
withTrustedRoots: true,
64+
)..setTrustedCertificatesBytes([/* bytes array */]),
65+
);
66+
67+
final client = Spinify(
68+
config: SpinifyConfig(
69+
client: (name: 'app', version: '1.0.0'),
70+
timeout: const Duration(seconds: 15),
71+
serverPingDelay: const Duration(seconds: 8),
72+
connectionRetryInterval: (
73+
min: const Duration(milliseconds: 250),
74+
max: const Duration(seconds: 15),
75+
),
76+
getToken: () async => '<token>',
77+
getPayload: () async => utf8.encode('Hello, World!'),
78+
codec: SpinifyProtobufCodec(),
79+
transportBuilder: SpinifyTransportAdapter.vm(
80+
compression: io.CompressionOptions.compressionDefault,
81+
customClient: httpClient,
82+
userAgent: 'Dart',
83+
),
84+
logger: (level, event, message, context) => print('[$event] $message'),
85+
),
86+
);
87+
```
88+
89+
Subscribe to a channel:
90+
91+
```dart
92+
final sub = client.newSubscription('notifications:index');
93+
sub.stream.publication().map((p) => utf8.decode(p.data)).listen(print);
94+
await sub.subscribe();
95+
await sub.publish(utf8.encode('Hello, World!'));
96+
await sub.unsubscribe();
97+
```
98+
99+
## Benchmarks
100+
101+
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.
102+
103+
Environment:
104+
105+
```
106+
Windows 11 Pro 64-bit
107+
CPU 13th Gen Intel Core i7-13700K
108+
Chrome Version 131.0.6778.86 (Official Build) (64-bit)
109+
Docker version 27.1.1
110+
Docker image centrifugo/centrifugo:v5
111+
Flutter 3.24.5 • Dart 3.5.4
112+
Package spinify v0.1.0
113+
Package centrifuge-dart v0.14.1
114+
```
115+
116+
The benchmark sends 10,000 messages of a certain size one after the other and measure the time.
117+
Each message is sent sequentially: the client waits for the server's response before sending the next message.
118+
119+
### Windows (Dart VM)
120+
121+
| | Spinify | Centrifuge-Dart |
122+
| ----- | ------------------- | ------------------- |
123+
| 1 KB | 5763 msg/s (7MB/s) | 5361 msg/s (6MB/s) |
124+
| 5 KB | 4405 msg/s (22MB/s) | 3731 msg/s (18MB/s) |
125+
| 10 KB | 3717 msg/s (37MB/s) | 2857 msg/s (28MB/s) |
126+
| 14 KB | 3305 msg/s (45MB/s) | 2564 msg/s (35MB/s) |
127+
| 16 KB | 3091 msg/s (50MB/s) | 1982 msg/s (32MB/s) |
128+
| 20 KB | 2812 msg/s (56MB/s) | 1811 msg/s (36MB/s) |
129+
| 30 KB | 2463 msg/s (72MB/s) | 1470 msg/s (43MB/s) |
130+
| 40 KB | 1937 msg/s (76MB/s) | 1089 msg/s (42MB/s) |
131+
| 50 KB | 1740 msg/s (85MB/s) | 967 msg/s (47MB/s) |
132+
| 60 KB | 1583 msg/s (92MB/s) | 877 msg/s (51MB/s) |
133+
134+
_\* Messages larger than 64 KB are not supported._
135+
136+
### Browser (WASM and JS)
137+
138+
| | Spinify WASM | Spinify JS | Centrifuge-Dart JS |
139+
| ----- | ------------------- | ------------------- | ------------------- |
140+
| 1 KB | 3676 msg/s (4MB/s) | 3502 msg/s (4MB/s) | 3067 msg/s (3MB/s) |
141+
| 5 KB | 2659 msg/s (13MB/s) | 3484 msg/s (17MB/s) | 2207 msg/s (11MB/s) |
142+
| 10 KB | 1926 msg/s (19MB/s) | 3189 msg/s (31MB/s) | 1584 msg/s (15MB/s) |
143+
| 14 KB | 1670 msg/s (22MB/s) | 2890 msg/s (39MB/s) | 1287 msg/s (17MB/s) |
144+
| 16 KB | 39 msg/s (662KB/s) | 39 msg/s (662KB/s) | 39 msg/s (662KB/s) |
145+
146+
_\* After message sizes exceed 15 KB, there is a noticeable performance drop._
147+
21148
## Features and Roadmap
22149

23150
- ✅ Connect to a server
@@ -54,8 +181,7 @@ dependencies:
54181
- ✅ Performance comparison with other libraries
55182
- ✅ WASM compatibility
56183
- ❌ 95% test coverage
57-
- ❌ JSON codec support
58-
- ❌ Flutter package
184+
- ❌ JSON codec support for transport
59185
- ❌ DevTools extension
60186
- ❌ Run in separate isolate
61187
- ❌ Middleware support
@@ -64,8 +190,6 @@ dependencies:
64190
- ❌ Optimistic subscriptions
65191
- ❌ Delta compression
66192

67-
## Example
68-
69193
## More resources
70194

71195
- [Library documentation](https://pub.dev/documentation/spinify/latest/)

example/benchmark/README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,36 @@
22

33
## Build
44

5+
JS
6+
7+
```bash
8+
flutter build web --release --no-source-maps --pwa-strategy offline-first --web-resources-cdn --base-href / --web-renderer canvaskit
9+
```
10+
11+
WASM
12+
13+
```bash
14+
flutter build web --release --no-source-maps --pwa-strategy offline-first --web-resources-cdn --base-href / --wasm
15+
```
16+
17+
## Serve
18+
19+
Windows
20+
21+
```bash
22+
flutter run -d windows --release
23+
```
24+
25+
JS
26+
27+
```bash
28+
flutter run -d chrome --release --web-resources-cdn --web-renderer canvaskit
29+
```
30+
31+
WASM
32+
533
```bash
6-
flutter build web --release --no-source-maps --pwa-strategy offline-first --web-renderer canvaskit --web-resources-cdn --base-href /
34+
flutter run -d chrome --release --web-resources-cdn --wasm
735
```
836

937
## Deploy

example/echo/main.dart

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,54 @@
1-
// ignore_for_file: avoid_print
1+
// ignore_for_file: avoid_print, implicit_call_tearoffs
22

3+
import 'dart:async';
34
import 'dart:io' as io;
45

56
import 'package:spinify/spinify.dart';
67

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

15+
final httpClient = io.HttpClient(
16+
context: io.SecurityContext(
17+
withTrustedRoots: true,
18+
), //..setTrustedCertificatesBytes([/* bytes array */])
19+
);
20+
1421
final client = Spinify(
1522
config: SpinifyConfig(
23+
client: (name: 'app', version: '1.0.0'),
24+
timeout: const Duration(seconds: 15),
25+
serverPingDelay: const Duration(seconds: 8),
26+
connectionRetryInterval: (
27+
min: const Duration(milliseconds: 250),
28+
max: const Duration(seconds: 15),
29+
),
30+
/* getToken: () async => '<token>', */
31+
/* getPayload: () async => utf8.encode('Hello, World!'), */
32+
codec: SpinifyProtobufCodec(),
33+
transportBuilder: SpinifyTransportAdapter.vm(
34+
compression: io.CompressionOptions.compressionDefault,
35+
customClient: httpClient,
36+
userAgent: 'Dart',
37+
),
1638
logger: (level, event, message, context) => print('[$event] $message'),
1739
),
1840
);
1941

42+
Timer(const Duration(minutes: 1), () async {
43+
await client.close();
44+
io.exit(0);
45+
});
46+
2047
var prev = client.state;
2148
client.states.listen((next) {
2249
print('$prev -> $next');
2350
prev = next;
2451
});
2552

26-
client.connect(url).ignore();
53+
await client.connect(url);
2754
}

lib/src/model/pubspec.yaml.g.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ sealed class Pubspec {
9393
static const PubspecVersion version = (
9494
/// Non-canonical string representation of the version as provided
9595
/// in the pubspec.yaml file.
96-
representation: r'0.1.0-pre.2',
96+
representation: r'0.1.0',
9797

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

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

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

120120
/// The build identifier: "foo" in "1.2.3+foo".
121121
build: <String>[],
@@ -125,12 +125,12 @@ sealed class Pubspec {
125125
static final DateTime timestamp = DateTime.utc(
126126
2024,
127127
11,
128-
21,
129-
14,
130-
36,
131-
47,
132-
322,
133-
307,
128+
28,
129+
18,
130+
51,
131+
31,
132+
283,
133+
52,
134134
);
135135

136136
/// Name

lib/src/spinify.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,8 @@ final class Spinify implements ISpinify {
287287
if (_refreshTimer != null) {
288288
warning('Health check failed: refresh timer set during connect');
289289
}
290-
case SpinifyState$Connected _:
291-
if (_refreshTimer == null) {
290+
case SpinifyState$Connected state:
291+
if (state.expires && _refreshTimer == null) {
292292
warning('Health check failed: no refresh timer set');
293293
_setUpRefreshConnection();
294294
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: >
44
Dart client to communicate with Centrifuge and Centrifugo from Dart and Flutter
55
over WebSockets with Protobuf support.
66
7-
version: 0.1.0-pre.2
7+
version: 0.1.0
88

99
homepage: https://centrifugal.dev
1010

0 commit comments

Comments
 (0)