Skip to content

Commit a946ece

Browse files
committed
Added gRPC Connection Pooling
- Added gRPC Connection Pooling - Added getMemberByExternalId - Updated SDK version - Edited ReadMe with Pooling Update
1 parent 9a5db94 commit a946ece

9 files changed

+169
-17
lines changed

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
bin/
3+
obj/
4+
certs/
5+
*.user
6+
*.suo
7+
*.userosscache
8+
*.sln.docstates
9+
*.csproj.user
10+
11+
project.lock.json
12+
project.fragment.lock.json
13+
artifacts/
14+
15+
.idea/
16+
.vscode/

Program.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
// Creates channel for connecting to PassKit server
2-
var channel = GrpcConnection.GrpcConnection.ConnectWithPassKitServer();
2+
//Single Connection
3+
//var channel = GrpcConnection.GrpcConnection.ConnectWithPassKitServer();
4+
//gRPC Connection Pooling
5+
var loyaltyChannel = GrpcConnectionPool.GrpcConnectionPool.GetInstance().GetChannel(); // Get a connection from the pool
6+
var couponChannel = GrpcConnectionPool.GrpcConnectionPool.GetInstance().GetChannel(); // Get a connection from the pool
7+
var flightChannel = GrpcConnectionPool.GrpcConnectionPool.GetInstance().GetChannel(); // Get a connection from the pool
8+
var ticketChannel = GrpcConnectionPool.GrpcConnectionPool.GetInstance().GetChannel(); // Get a connection from the pool
9+
310

411
// Loyalty Quickstart
512
QuickstartLoyalty.Membership buildLoyalty = new QuickstartLoyalty.Membership();
6-
buildLoyalty.Quickstart(channel);
13+
buildLoyalty.Quickstart(loyaltyChannel);
714

815
// Coupons Quickstart
916
QuickstartCoupons.Coupons buildCoupons = new QuickstartCoupons.Coupons();
10-
buildCoupons.Quickstart(channel);
17+
buildCoupons.Quickstart(couponChannel);
1118

1219
// Flight Quickstart
1320
QuickstartFlightTickets.FlightTickets buildFlights = new QuickstartFlightTickets.FlightTickets();
14-
buildFlights.QuickStart(channel);
21+
buildFlights.QuickStart(flightChannel);
1522

1623
// Event Tickets Quickstart
1724
QuickstartEventickets.EventTicket buildEventTickets = new QuickstartEventickets.EventTicket();
18-
buildEventTickets.QuickStart(channel);
25+
buildEventTickets.QuickStart(ticketChannel);

Quickstarts/GrpcConnectionPooling.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Threading;
6+
using Grpc.Core;
7+
8+
namespace GrpcConnectionPool
9+
{
10+
class GrpcConnectionPool
11+
{
12+
private static readonly object _lock = new object();
13+
private static GrpcConnectionPool _instance;
14+
private readonly List<Channel> _channelPool;
15+
private int _currentIndex;
16+
private readonly int _poolSize;
17+
18+
private GrpcConnectionPool(int poolSize = 5)
19+
{
20+
_poolSize = poolSize;
21+
_channelPool = new List<Channel>();
22+
23+
for (int i = 0; i < _poolSize; i++)
24+
{
25+
_channelPool.Add(CreateChannel());
26+
}
27+
}
28+
29+
public static GrpcConnectionPool GetInstance(int poolSize = 5)
30+
{
31+
if (_instance == null)
32+
{
33+
lock (_lock)
34+
{
35+
if (_instance == null)
36+
{
37+
_instance = new GrpcConnectionPool(poolSize);
38+
}
39+
}
40+
}
41+
return _instance;
42+
}
43+
44+
private Channel CreateChannel()
45+
{
46+
var host = "grpc.pub1.passkit.io";
47+
var port = 443;
48+
49+
try
50+
{
51+
var rootCert = File.ReadAllText("certs/ca-chain.pem");
52+
var keyFile = File.ReadAllText("certs/key.pem");
53+
var certFile = File.ReadAllText("certs/certificate.pem");
54+
55+
var keyCertPair = new KeyCertificatePair(certFile, keyFile);
56+
var credentials = new SslCredentials(rootCert, keyCertPair);
57+
58+
return new Channel(host, port, credentials);
59+
}
60+
catch (IOException e)
61+
{
62+
throw new Exception($"Failed to read certificate files: {e.Message}", e);
63+
}
64+
catch (RpcException e)
65+
{
66+
throw new RpcException(new Status(e.Status.StatusCode, e.Status.Detail));
67+
}
68+
}
69+
70+
public Channel GetChannel()
71+
{
72+
lock (_lock)
73+
{
74+
var channel = _channelPool[_currentIndex];
75+
_currentIndex = (_currentIndex + 1) % _poolSize;
76+
return channel;
77+
}
78+
}
79+
80+
public void Shutdown()
81+
{
82+
lock (_lock)
83+
{
84+
foreach (var channel in _channelPool)
85+
{
86+
channel.ShutdownAsync().Wait();
87+
}
88+
_channelPool.Clear();
89+
}
90+
}
91+
}
92+
}

Quickstarts/QuickstartCoupons.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Coupons
4040
* - Redeem a coupon
4141
* - Deletes campaign
4242
*/
43-
public void Quickstart(Channel channel)
43+
public void Quickstart(Grpc.Core.Channel channel)
4444
{
4545
createStubs(channel);
4646
createTemplate();
@@ -54,7 +54,7 @@ public void Quickstart(Channel channel)
5454
// always close the channel when there will be no further calls made.
5555
channel.ShutdownAsync().Wait();
5656
}
57-
private void createStubs(Channel channel)
57+
private void createStubs(Grpc.Core.Channel channel)
5858
{
5959
templatesStub = new Templates.TemplatesClient(channel);
6060
couponsStub = new SingleUseCoupons.SingleUseCouponsClient(channel);
@@ -111,7 +111,7 @@ private void createOffer()
111111
offer.OfferShortTitle = "Base Offer";
112112
offer.OfferDetails = "Base Offer";
113113
offer.IssueStartDate = DateTime.UtcNow.ToTimestamp();
114-
offer.IssueEndDate = new DateTime(2022, 06, 30).ToUniversalTime().ToTimestamp();
114+
offer.IssueEndDate = new DateTime(2025, 06, 30).ToUniversalTime().ToTimestamp();
115115

116116
baseOfferId = couponsStub.createCouponOffer(offer);
117117
Console.WriteLine("Created base offer, base offer id is " + baseOfferId.Id_);

Quickstarts/QuickstartEventTickets.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class EventTicket
4343
public static PassKit.Grpc.Id ticketTypeId;
4444
public static PassKit.Grpc.Id pass;
4545

46-
public void QuickStart(Channel channel)
46+
public void QuickStart(Grpc.Core.Channel channel)
4747
{
4848
createStubs(channel);
4949
createTemplate();
@@ -59,7 +59,7 @@ public void QuickStart(Channel channel)
5959

6060
}
6161

62-
private void createStubs(Channel channel)
62+
private void createStubs(Grpc.Core.Channel channel)
6363
{
6464
templatesStub = new Templates.TemplatesClient(channel);
6565
eventsStub = new EventTickets.EventTicketsClient(channel);
@@ -168,8 +168,11 @@ private void issueEventTicket()
168168

169169
pass = eventsStub.issueTicket(ticket);
170170
Console.WriteLine("Event ticket created, event ticket url: https://pub1.pskt.io/" + pass.Id_);
171+
171172
}
172173

174+
175+
173176
private void validateTicket()
174177
{
175178
DateTime validateDate = DateTime.Now;

Quickstarts/QuickstartFlights.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class FlightTickets
3939
public static PassKit.Grpc.Id templateId;
4040
public static BoardingPassesResponse pass;
4141

42-
public void QuickStart(Channel channel)
42+
public void QuickStart(Grpc.Core.Channel channel)
4343
{
4444
createStubs(channel);
4545
createTemplates();
@@ -54,7 +54,7 @@ public void QuickStart(Channel channel)
5454

5555
}
5656

57-
private void createStubs(Channel channel)
57+
private void createStubs(Grpc.Core.Channel channel)
5858
{
5959
templatesStub = new Templates.TemplatesClient(channel);
6060
flightsStub = new Flights.FlightsClient(channel);

Quickstarts/QuickstartLoyalty.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Membership
2626
public static PassKit.Grpc.Id vipMemberId;
2727
public static PassKit.Grpc.Id baseTemplateId;
2828
public static PassKit.Grpc.Id vipTemplateId;
29+
public static String externalId = "12345";
2930
public static String baseEmail = "[email protected]"; // Change to your email to receive cards
3031
public static String vipEmail = "[email protected]"; // Change to your email to receive cards
3132

@@ -42,13 +43,14 @@ class Membership
4243
*- Add loyalty points to a member
4344
*- Delete all membership assets
4445
*/
45-
public void Quickstart(Channel channel)
46+
public void Quickstart(Grpc.Core.Channel channel)
4647
{
4748
createStubs(channel);
4849
createTemplate();
4950
createProgram();
5051
createTier();
5152
enrolMember();
53+
getMemberByExternalId();
5254
checkInMember(); //optional
5355
checkOutMember(); //optional
5456
addPoints(); //optional
@@ -59,7 +61,7 @@ public void Quickstart(Channel channel)
5961

6062

6163
}
62-
private void createStubs(Channel channel)
64+
private void createStubs(Grpc.Core.Channel channel)
6365
{
6466
templatesStub = new Templates.TemplatesClient(channel);
6567
membersStub = new Members.MembersClient(channel);
@@ -149,6 +151,7 @@ private void enrolMember()
149151
// Enrolls member on vip tier
150152
Console.WriteLine("Enrolling member on vip tier");
151153
member.TierId = vipTierId.Id_;
154+
member.ExternalId = externalId;
152155
member.Points = 9999;
153156
member.Person.Surname = "Highroller";
154157
member.Person.Forename = "Harry";
@@ -233,5 +236,27 @@ private void deleteProgram()
233236
membersStub.deleteProgram(deleteProgramId);
234237
Console.WriteLine("Deleted program ");
235238
}
239+
240+
private void getMemberByExternalId()
241+
{
242+
Console.WriteLine("Getting member by external Id");
243+
try
244+
{
245+
MemberRecordByExternalIdRequest memberRequest = new() { ProgramId = programId.Id_, ExternalId = externalId };
246+
247+
var member = membersStub.getMemberRecordByExternalId(memberRequest);
248+
if (member != null)
249+
{
250+
Console.WriteLine("Found Member: " + member);
251+
}
252+
else
253+
{
254+
Console.WriteLine("Member Not Found");
255+
}
256+
}
257+
catch (Exception ex)
258+
{ Console.WriteLine(ex.ToString()); }
259+
260+
}
236261
}
237262
}

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ You will need the following:
1919
- Recommended Code Editor Visual Studio Code [Guide to Installation](https://code.visualstudio.com/docs/setup/setup-overview)
2020
- If you use Visual Studio Code use this [guide](https://code.visualstudio.com/docs/languages/csharp) to install the necessary extensions
2121
- .NET v6.0 [Guide to Installation](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) (please note: should you be installing this in Mac if may come up as malicious software despite being from Microsoft. The way to fix this is shown in the configuration)
22+
- Your [SDK Host and Port](https://help.passkit.com/en/articles/10720707-sdk-and-api-configuration-settings), you may need to update this in the `GrpcConnection.cs` or `GrpcConnectionPooling.cs` depending on which server you signed up on
2223

2324
### Configuration
2425

@@ -39,9 +40,11 @@ You will need the following:
3940
and lines 30 and 31 in QuickstartCoupons for coupons.
4041
![ScreenShot](images/coupon-email.png)
4142

42-
4. Go back to root directory with `cd ../`. Then run `dotnet run`, to create a sample membership card, coupon card and boarding pass (with default templates & tiers/offers) and issue them.
43+
4. Go back to root directory with `cd ../`. Then run `dotnet restore` to download any missing dependencies, followed by `dotnet build` to compile the project.
4344

44-
5. Mac users only - After configuring the quickstart should you encounter the error, navigate to system preferences and select security and privacy.
45+
5. Finally `dotnet run`, to create a sample membership card, coupon card and boarding pass (with default templates & tiers/offers) and issue them.
46+
47+
6. Mac users only - After configuring the quickstart should you encounter the error, navigate to system preferences and select security and privacy.
4548
![ScreenShot](images/preferences.png)
4649
Then navigate to general.
4750
![ScreenShot](images/security.png)
@@ -50,6 +53,12 @@ In general, underneath App Store and identified developers the file that is bein
5053

5154
## Examples
5255
All quickstarts are found in the Quickstarts folder.
56+
### GRPC Connection
57+
For implementing in your own projects, use the `GrpcConnectionPool` or `GrpcConnection` class to manage connections to the PassKit gRPC endpoints. By default, this quickstart uses gRPC connection pooling to optimize performance and manage multiple requests efficiently. However, if you prefer to use the single-connection method, comment out the pooling line and use the regular connection method line in `Program.cs`
58+
When to Use Each Mehod:
59+
- gRPC Connection Pooling (Default) - Recommended for production environments with high concurrency and multiple requests. Improves performance by reusing connections.
60+
- Single Connection - Useful for simple applications, debugging, or when only a few API calls are needed.
61+
5362
### Membership Cards
5463
QuickstartLoyalty will create a membership program with 2 tiers, base and VIP. It will enrol two members, one in each tier.
5564
It contains the methods:

c-sharp-quickstart.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="PassKit.Grpc" Version="1.1.70" />
12+
<PackageReference Include="PassKit.Grpc" Version="1.1.123" />
1313
</ItemGroup>
1414

1515
</Project>

0 commit comments

Comments
 (0)