Skip to content

Commit 9dd31a3

Browse files
mohitpubnubMohit Tejanipubnub-release-bot
authored
fix/heartbet call on subscription change (#251)
* Handling heartbeat and leave calls for event engine * * removed acceptance test for EE to make it compatible with current feature tests, Added tests to ensure subscription with EE, join events emitted through heartbeat calls * refactoring pubnub.cs and added heartbeat and leave for reconnect and disconnect * added missing configureAwait * refactor: removed ignored response for heartbeat and leave calls, test refactoring for heartbeat calls on subscribe() * fix: join event test on getting ack on connected event * test-refactor: cleanup for presence event accuracy in ee tests * refactor test for handling 0 presence interval case * PubNub SDK v7.3.11.0 release. * fix(test): presence test flakiness --------- Co-authored-by: Mohit Tejani <[email protected]> Co-authored-by: PubNub Release Bot <[email protected]>
1 parent 76cc28c commit 9dd31a3

File tree

16 files changed

+473
-825
lines changed

16 files changed

+473
-825
lines changed

.pubnub.yml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
name: c-sharp
2-
version: "7.3.10"
2+
version: "7.3.11"
33
schema: 1
44
scm: github.com/pubnub/c-sharp
55
changelog:
6+
- date: 2025-05-20
7+
version: v7.3.11
8+
changes:
9+
- type: bug
10+
text: "Fix missing `heartbeat` and `leave` REST API calls when the event engine is enabled and `presenceTimeout` or `presenceHeartbeatInterval` not set."
611
- date: 2025-05-09
712
version: v7.3.10
813
changes:
@@ -897,7 +902,7 @@ features:
897902
- QUERY-PARAM
898903
supported-platforms:
899904
-
900-
version: Pubnub 'C#' 7.3.10
905+
version: Pubnub 'C#' 7.3.11
901906
platforms:
902907
- Windows 10 and up
903908
- Windows Server 2008 and up
@@ -908,7 +913,7 @@ supported-platforms:
908913
- .Net Framework 4.6.1+
909914
- .Net Framework 6.0
910915
-
911-
version: PubnubPCL 'C#' 7.3.10
916+
version: PubnubPCL 'C#' 7.3.11
912917
platforms:
913918
- Xamarin.Android
914919
- Xamarin.iOS
@@ -928,7 +933,7 @@ supported-platforms:
928933
- .Net Core
929934
- .Net 6.0
930935
-
931-
version: PubnubUWP 'C#' 7.3.10
936+
version: PubnubUWP 'C#' 7.3.11
932937
platforms:
933938
- Windows Phone 10
934939
- Universal Windows Apps
@@ -952,7 +957,7 @@ sdks:
952957
distribution-type: source
953958
distribution-repository: GitHub
954959
package-name: Pubnub
955-
location: https://github.com/pubnub/c-sharp/releases/tag/v7.3.10.0
960+
location: https://github.com/pubnub/c-sharp/releases/tag/v7.3.11.0
956961
requires:
957962
-
958963
name: ".Net"
@@ -1235,7 +1240,7 @@ sdks:
12351240
distribution-type: source
12361241
distribution-repository: GitHub
12371242
package-name: PubNubPCL
1238-
location: https://github.com/pubnub/c-sharp/releases/tag/v7.3.10.0
1243+
location: https://github.com/pubnub/c-sharp/releases/tag/v7.3.11.0
12391244
requires:
12401245
-
12411246
name: ".Net Core"
@@ -1594,7 +1599,7 @@ sdks:
15941599
distribution-type: source
15951600
distribution-repository: GitHub
15961601
package-name: PubnubUWP
1597-
location: https://github.com/pubnub/c-sharp/releases/tag/v7.3.10.0
1602+
location: https://github.com/pubnub/c-sharp/releases/tag/v7.3.11.0
15981603
requires:
15991604
-
16001605
name: "Universal Windows Platform Development"

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
v7.3.11 - May 20 2025
2+
-----------------------------
3+
- Fixed: fix missing `heartbeat` and `leave` REST API calls when the event engine is enabled and `presenceTimeout` or `presenceHeartbeatInterval` not set.
4+
15
v7.3.10 - May 09 2025
26
-----------------------------
37
- Fixed: specified dependency version for fixing synk reported issues.

src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class SubscribeEndpoint<T>: ISubscribeOperation<T>
2626
private SubscribeEventEngine subscribeEventEngine;
2727
private SubscribeEventEngineFactory subscribeEventEngineFactory;
2828
private PresenceOperation<T> presenceOperation;
29+
private HeartbeatOperation heartbeatOperation;
30+
private string[] inputChannels = [];
31+
private string[] inputChannelGroups = [];
2932
private string instanceId { get; set; }
3033
public EventEmitter EventEmitter { get; set; }
3134
public List<SubscribeCallback> SubscribeListenerList
@@ -34,7 +37,7 @@ public List<SubscribeCallback> SubscribeListenerList
3437
set;
3538
} = new List<SubscribeCallback>();
3639

37-
public SubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, TokenManager tokenManager,SubscribeEventEngineFactory subscribeEventEngineFactory, PresenceOperation<T> presenceOperation , string instanceId, Pubnub instance)
40+
public SubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, TokenManager tokenManager,SubscribeEventEngineFactory subscribeEventEngineFactory, PresenceOperation<T> presenceOperation , HeartbeatOperation heartbeatOperation ,string instanceId, Pubnub instance)
3841
{
3942
PubnubInstance = instance;
4043
config = pubnubConfig;
@@ -44,12 +47,16 @@ public SubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jso
4447
this.subscribeEventEngineFactory = subscribeEventEngineFactory;
4548
this.presenceOperation = presenceOperation;
4649
this.instanceId = instanceId;
50+
this.heartbeatOperation = heartbeatOperation;
4751
if (unit != null) { unit.EventTypeList = new List<KeyValuePair<string, string>>(); }
4852
}
4953

5054
public ISubscribeOperation<T> Channels(string[] channels)
5155
{
52-
if (channels != null && channels.Length > 0 && !string.IsNullOrEmpty(channels[0]))
56+
inputChannels = channels.ToList<string>()
57+
.Except(subscribeChannelNames)
58+
.Where(c => !c.EndsWith(Constants.Pnpres, StringComparison.OrdinalIgnoreCase)).ToArray();
59+
if (channels.Length > 0 && !string.IsNullOrEmpty(channels[0]))
5360
{
5461
this.subscribeChannelNames.AddRange(channels);
5562
}
@@ -58,7 +65,10 @@ public ISubscribeOperation<T> Channels(string[] channels)
5865

5966
public ISubscribeOperation<T> ChannelGroups(string[] channelGroups)
6067
{
61-
if (channelGroups != null && channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0]))
68+
inputChannelGroups = channelGroups.ToList<string>()
69+
.Except(channelGroups)
70+
.Where(cg => !cg.EndsWith(Constants.Pnpres, StringComparison.OrdinalIgnoreCase)).ToArray();
71+
if (channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0]))
6272
{
6373
this.subscribeChannelGroupNames.AddRange(channelGroups);
6474
}
@@ -113,24 +123,44 @@ public void Execute()
113123
Subscribe(channelNames, channelGroupNames, cursor, this.queryParam);
114124
}
115125

116-
private void Subscribe(string[] channels, string[] channelGroups, SubscriptionCursor cursor, Dictionary<string, object> externalQueryParam)
126+
private async void Subscribe(string[] channels, string[] channelGroups, SubscriptionCursor cursor, Dictionary<string, object> externalQueryParam)
117127
{
118-
if ((channels?.Length ?? 0) == 0 && (channelGroups?.Length ?? 0) == 0) {
119-
throw new ArgumentException("Either Channel Or Channel Group or Both should be provided.");
120-
}
128+
try
129+
{
130+
if ((channels?.Length ?? 0) == 0 && (channelGroups?.Length ?? 0) == 0) {
131+
throw new ArgumentException("Either Channel Or Channel Group or Both should be provided.");
132+
}
121133

122-
if (subscribeEventEngineFactory.HasEventEngine(instanceId)) {
123-
subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(instanceId);
124-
} else {
125-
var subscribeManager = new SubscribeManager2(config, jsonLibrary, unit, pubnubTokenMgr, PubnubInstance);
126-
subscribeEventEngine = subscribeEventEngineFactory.InitializeEventEngine(instanceId, PubnubInstance, config, subscribeManager, this.EventEmitter, jsonLibrary, StatusEmitter);
127-
subscribeEventEngine.OnStateTransition += SubscribeEventEngine_OnStateTransition;
128-
subscribeEventEngine.OnEventQueued += SubscribeEventEngine_OnEventQueued;
129-
subscribeEventEngine.OnEffectDispatch += SubscribeEventEngine_OnEffectDispatch;
134+
if (subscribeEventEngineFactory.HasEventEngine(instanceId)) {
135+
subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(instanceId);
136+
} else {
137+
var subscribeManager = new SubscribeManager2(config, jsonLibrary, unit, pubnubTokenMgr, PubnubInstance);
138+
subscribeEventEngine = subscribeEventEngineFactory.InitializeEventEngine(instanceId, PubnubInstance, config, subscribeManager, this.EventEmitter, jsonLibrary, StatusEmitter);
139+
subscribeEventEngine.OnStateTransition += SubscribeEventEngine_OnStateTransition;
140+
subscribeEventEngine.OnEventQueued += SubscribeEventEngine_OnEventQueued;
141+
subscribeEventEngine.OnEffectDispatch += SubscribeEventEngine_OnEffectDispatch;
142+
}
143+
subscribeEventEngine.Subscribe<T>(channels, channelGroups, cursor);
144+
if (this.presenceOperation == null)
145+
{
146+
if (inputChannels.Any() || inputChannelGroups.Any())
147+
{
148+
await heartbeatOperation.HeartbeatRequest<string>(
149+
inputChannels,
150+
inputChannelGroups
151+
).ConfigureAwait(false);
152+
}
153+
}
154+
if (this.presenceOperation != null) {
155+
presenceOperation.Start(channels?.Where(c => !c.EndsWith(Constants.Pnpres)).ToArray(), channelGroups?.Where(cg => !cg.EndsWith(Constants.Pnpres)).ToArray());
156+
}
157+
// clear incoming entity list to not mix names with subsequent subscribe() calls.
158+
inputChannels =[];
159+
inputChannelGroups=[];
130160
}
131-
subscribeEventEngine.Subscribe<T>(channels, channelGroups, cursor);
132-
if (this.presenceOperation != null) {
133-
presenceOperation.Start(channels?.Where(c => !c.EndsWith(Constants.Pnpres)).ToArray(), channelGroups?.Where(cg => !cg.EndsWith(Constants.Pnpres)).ToArray());
161+
catch (Exception e)
162+
{
163+
config?.Logger?.Error($"Subscribe request encounter error: {e.Message}\n{e.StackTrace}");
134164
}
135165
}
136166

src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,24 @@ namespace PubnubApi.EndPoint
1111
public class UnsubscribeEndpoint<T> : PubnubCoreBase, IUnsubscribeOperation<T>
1212
{
1313
private readonly PNConfiguration config;
14-
private readonly IJsonPluggableLibrary jsonLibrary;
15-
private readonly IPubnubUnitTest unit;
16-
17-
private readonly EndPoint.TokenManager pubnubTokenMgr;
18-
1914
private string[] subscribeChannelNames;
2015
private string[] subscribeChannelGroupNames;
2116
private Dictionary<string, object> queryParam { get; set; }
2217
private Pubnub pubnubInstance { get; set; }
2318
private SubscribeEventEngine subscribeEventEngine { get; set; }
2419
private SubscribeEventEngineFactory subscribeEventEngineFactory { get; set; }
2520
private PresenceEventEngineFactory presenceEventEngineFactory;
26-
private string instanceId { get; set; }
21+
private LeaveOperation leaveOperation;
22+
private string instanceId { get; }
2723

28-
public UnsubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, EndPoint.TokenManager tokenManager, SubscribeEventEngineFactory subscribeEventEngineFactory, PresenceEventEngineFactory presenceEventEngineFactory, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, tokenManager, instance)
24+
public UnsubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, EndPoint.TokenManager tokenManager, LeaveOperation leaveOperation,SubscribeEventEngineFactory subscribeEventEngineFactory, PresenceEventEngineFactory presenceEventEngineFactory, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, tokenManager, instance)
2925
{
3026
pubnubInstance = instance;
3127
config = pubnubConfig;
32-
jsonLibrary = jsonPluggableLibrary;
33-
unit = pubnubUnit;
3428

35-
pubnubTokenMgr = tokenManager;
3629
this.subscribeEventEngineFactory = subscribeEventEngineFactory;
3730
this.presenceEventEngineFactory = presenceEventEngineFactory;
31+
this.leaveOperation = leaveOperation;
3832
instanceId = instance.InstanceId;
3933
}
4034

@@ -62,7 +56,7 @@ public void Execute()
6256
Unsubscribe(subscribeChannelNames, subscribeChannelGroupNames);
6357
}
6458

65-
private void Unsubscribe(string[] channels, string[] channelGroups)
59+
private async void Unsubscribe(string[] channels, string[] channelGroups)
6660
{
6761
if ((channels == null || channels.Length == 0) && (channelGroups == null || channelGroups.Length == 0)) {
6862
throw new ArgumentException("Either Channel Or Channel Group or Both should be provided.");
@@ -120,6 +114,14 @@ private void Unsubscribe(string[] channels, string[] channelGroups)
120114
{ Channels = uniqueChannelsToRemove.ToArray(), ChannelGroups = uniqueChannelGroupsToRemove.ToArray() }
121115
});
122116
}
117+
else
118+
{
119+
if(!config.SuppressLeaveEvents)
120+
await leaveOperation.LeaveRequest<string>(
121+
uniqueChannelsToRemove.Distinct().ToArray(),
122+
uniqueChannelGroupsToRemove.Distinct().ToArray()
123+
).ConfigureAwait(false);
124+
}
123125
if (config.MaintainPresenceState) {
124126
if (ChannelLocalUserState.TryGetValue(PubnubInstance.InstanceId, out
125127
var userState)) {

src/Api/PubnubApi/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
[assembly: AssemblyProduct("Pubnub C# SDK")]
1212
[assembly: AssemblyCopyright("Copyright © 2021")]
1313
[assembly: AssemblyTrademark("")]
14-
[assembly: AssemblyVersion("7.3.10.0")]
15-
[assembly: AssemblyFileVersion("7.3.10.0")]
14+
[assembly: AssemblyVersion("7.3.11.0")]
15+
[assembly: AssemblyFileVersion("7.3.11.0")]
1616
// Setting ComVisible to false makes the types in this assembly not visible
1717
// to COM components. If you need to access a type in this assembly from
1818
// COM, set the ComVisible attribute to true on that type.

0 commit comments

Comments
 (0)