Skip to content

Commit

Permalink
feat: Add SetLength API
Browse files Browse the repository at this point in the history
  • Loading branch information
rishtigupta committed Jun 21, 2023
1 parent d1fd4da commit 6c60992
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 1 deletion.
17 changes: 16 additions & 1 deletion src/Momento.Sdk/CacheClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using Microsoft.Extensions.Logging;
using Momento.Sdk.Auth;
using Momento.Sdk.Config;
Expand Down Expand Up @@ -900,6 +899,22 @@ public async Task<CacheSetFetchResponse> SetFetchAsync(string cacheName, string

return await this.DataClient.SetFetchAsync(cacheName, setName);
}

/// <inheritdoc />
public async Task<CacheSetLengthResponse> SetLengthAsync(string cacheName, string setName)
{
try
{
Utils.ArgumentNotNull(cacheName, nameof(cacheName));
Utils.ArgumentNotNull(setName, nameof(setName));
}
catch (ArgumentNullException e)
{
return new CacheSetLengthResponse.Error(new InvalidArgumentException(e.Message));
}

return await this.DataClient.SetLengthAsync(cacheName, setName);
}

/// <inheritdoc />
public async Task<CacheListConcatenateFrontResponse> ListConcatenateFrontAsync(string cacheName, string listName, IEnumerable<byte[]> values, int? truncateBackToSize = null, CollectionTtl ttl = default(CollectionTtl))
Expand Down
10 changes: 10 additions & 0 deletions src/Momento.Sdk/ICacheClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,16 @@ public interface ICacheClient : IDisposable
/// <param name="setName">The set to fetch.</param>
/// <returns>Task representing with the status of the fetch operation and the associated set.</returns>
public Task<CacheSetFetchResponse> SetFetchAsync(string cacheName, string setName);

/// <summary>
/// Calculate the length of a set in the cache.
///
/// A set that does not exist is interpreted to have length 0.
/// </summary>
/// <param name="cacheName">Name of the cache to perform the lookup in.</param>
/// <param name="setName">The dictionary to calculate length.</param>
/// <returns>Task representing the length of the dictionary.</returns>
public Task<CacheSetLengthResponse> SetLengthAsync(string cacheName, string setName);

/// <summary>
/// Push multiple values to the beginning of a list.
Expand Down
7 changes: 7 additions & 0 deletions src/Momento.Sdk/Internal/DataGrpcManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public interface IDataClient
public Task<_SetUnionResponse> SetUnionAsync(_SetUnionRequest request, CallOptions callOptions);
public Task<_SetDifferenceResponse> SetDifferenceAsync(_SetDifferenceRequest request, CallOptions callOptions);
public Task<_SetFetchResponse> SetFetchAsync(_SetFetchRequest request, CallOptions callOptions);
public Task<_SetLengthResponse> SetLengthAsync(_SetLengthRequest request, CallOptions callOptions);
public Task<_ListPushFrontResponse> ListPushFrontAsync(_ListPushFrontRequest request, CallOptions callOptions);
public Task<_ListPushBackResponse> ListPushBackAsync(_ListPushBackRequest request, CallOptions callOptions);
public Task<_ListPopFrontResponse> ListPopFrontAsync(_ListPopFrontRequest request, CallOptions callOptions);
Expand Down Expand Up @@ -166,6 +167,12 @@ public async Task<_SetFetchResponse> SetFetchAsync(_SetFetchRequest request, Cal
var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.SetFetchAsync(r, o));
return await wrapped.ResponseAsync;
}

public async Task<_SetLengthResponse> SetLengthAsync(_SetLengthRequest request, CallOptions callOptions)
{
var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.SetLengthAsync(r, o));
return await wrapped.ResponseAsync;
}

public async Task<_ListPushFrontResponse> ListPushFrontAsync(_ListPushFrontRequest request, CallOptions callOptions)
{
Expand Down
30 changes: 30 additions & 0 deletions src/Momento.Sdk/Internal/ScsDataClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ public async Task<CacheSetFetchResponse> SetFetchAsync(string cacheName, string
{
return await SendSetFetchAsync(cacheName, setName);
}

public async Task<CacheSetLengthResponse> SetLengthAsync(string cacheName, string setName)
{
return await SendSetLengthAsync(cacheName, setName);
}

public async Task<CacheListConcatenateFrontResponse> ListConcatenateFrontAsync(string cacheName, string listName, IEnumerable<byte[]> values, int? truncateBackToSize = null, CollectionTtl ttl = default(CollectionTtl))
{
Expand Down Expand Up @@ -934,6 +939,31 @@ private async Task<CacheSetFetchResponse> SendSetFetchAsync(string cacheName, st

return this._logger.LogTraceCollectionRequestSuccess(REQUEST_TYPE_SET_FETCH, cacheName, setName, new CacheSetFetchResponse.Miss());
}

const string REQUEST_TYPE_SET_LENGTH = "SET_LENGTH";
private async Task<CacheSetLengthResponse> SendSetLengthAsync(string cacheName, string setName)
{
_SetLengthRequest request = new() { SetName = setName.ToByteString() };
_SetLengthResponse response;
var metadata = MetadataWithCache(cacheName);

try
{
this._logger.LogTraceExecutingCollectionRequest(REQUEST_TYPE_SET_LENGTH, cacheName, setName);
response = await this.grpcManager.Client.SetLengthAsync(request, new CallOptions(headers: MetadataWithCache(cacheName), deadline: CalculateDeadline()));
}
catch (Exception e)
{
return this._logger.LogTraceCollectionRequestError(REQUEST_TYPE_SET_LENGTH, cacheName, setName, new CacheSetLengthResponse.Error(_exceptionMapper.Convert(e, metadata)));
}

if (response.SetCase == _SetLengthResponse.SetOneofCase.Missing)
{
return this._logger.LogTraceCollectionRequestSuccess(REQUEST_TYPE_SET_LENGTH, cacheName, setName, new CacheSetLengthResponse.Miss());
}

return this._logger.LogTraceCollectionRequestSuccess(REQUEST_TYPE_SET_LENGTH, cacheName, setName, new CacheSetLengthResponse.Hit(response));
}

const string REQUEST_TYPE_LIST_CONCATENATE_FRONT = "LIST_CONCATENATE_FRONT";

Expand Down
105 changes: 105 additions & 0 deletions src/Momento.Sdk/Responses/CacheSetLengthResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Momento.Protos.CacheClient;
using Momento.Sdk.Exceptions;

namespace Momento.Sdk.Responses;

/// <summary>
/// Parent response type for a cache set length request. The
/// response object is resolved to a type-safe object of one of
/// the following subtypes:
/// <list type="bullet">
/// <item><description>CacheSetLengthResponse.Hit</description></item>
/// <item><description>CacheSetLengthResponse.Miss</description></item>
/// <item><description>CacheSetLengthResponse.Error</description></item>
/// </list>
/// Pattern matching can be used to operate on the appropriate subtype.
/// For example:
/// <code>
/// if (response is CacheSetLengthResponse.Hit hitResponse)
/// {
/// return hitResponse.Length;
/// }
/// else if (response is CacheSetLengthResponse.Miss missResponse)
/// {
/// // handle missResponse as appropriate
/// }
/// else if (response is CacheSetLengthResponse.Error errorResponse)
/// {
/// // handle error as appropriate
/// }
/// else
/// {
/// // handle unexpected response
/// }
/// </code>
/// </summary>
public abstract class CacheSetLengthResponse
{
/// <include file="../docs.xml" path='docs/class[@name="Hit"]/description/*' />
public class Hit : CacheSetLengthResponse
{
/// <summary>
/// The length of the set. If the set is missing or empty, the result is zero.
/// </summary>
public int Length { get; private set; } = 0;

/// <summary>
///
/// </summary>
/// <param name="response">The cache response.</param>
public Hit(_SetLengthResponse response)
{
if(response.SetCase == _SetLengthResponse.SetOneofCase.Found) {
Length = checked((int)response.Found.Length);
}
}

/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()}: Length: {Length}";
}
}

/// <include file="../docs.xml" path='docs/class[@name="Miss"]/description/*' />
public class Miss : CacheSetLengthResponse
{

}

/// <include file="../docs.xml" path='docs/class[@name="Error"]/description/*' />
public class Error : CacheSetLengthResponse, IError
{
private readonly SdkException _error;

/// <include file="../docs.xml" path='docs/class[@name="Error"]/constructor/*' />
public Error(SdkException error)
{
_error = error;
}

/// <inheritdoc />
public SdkException InnerException
{
get => _error;
}

/// <inheritdoc />
public MomentoErrorCode ErrorCode
{
get => _error.ErrorCode;
}

/// <inheritdoc />
public string Message
{
get => $"{_error.MessageWrapper}: {_error.Message}";
}

/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()}: {this.Message}";
}
}
}
22 changes: 22 additions & 0 deletions tests/Integration/Momento.Sdk.Tests/SetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -608,4 +608,26 @@ public async Task SetDeleteAsync_SetExists_HappyPath()
Assert.True(deleteResponse is CacheDeleteResponse.Success, $"Unexpected response: {deleteResponse}");
Assert.True(await client.SetFetchAsync(cacheName, setName) is CacheSetFetchResponse.Miss);
}

[Fact]
public async Task SetLengthAsync_SetIsMissing()
{
var setName = Utils.NewGuidString();
CacheSetLengthResponse response = await client.SetLengthAsync(cacheName, setName);
Assert.True(response is CacheSetLengthResponse.Miss, $"Unexpected response: {response}");
}

[Fact]
public async Task SetLengthAsync_HappyPath()
{
var setName = Utils.NewGuidString();
var element = Utils.NewGuidByteArray();

await client.SetAddElementAsync(cacheName, setName, element);

CacheSetLengthResponse lengthResponse = await client.SetLengthAsync(cacheName, setName);
Assert.True(lengthResponse is CacheSetLengthResponse.Hit, $"Unexpected response: {lengthResponse}");
var hitResponse = (CacheSetLengthResponse.Hit)lengthResponse;
Assert.Equal(1, hitResponse.Length);
}
}

0 comments on commit 6c60992

Please sign in to comment.