I want to implement RedisLock usning StackExchange.Redis library.
By following this article:
https://www.c-sharpcorner.com/article/creating-distributed-lock-with-redis-in-net-core/
How do I need to block a Redis stream.
How do I need to unlock Redis stream. Do I really need to use a script to remove an object from a stream, maybe another programm want to handle current element?
Problem with runing script, but every pass of the loop the programm can't realise the lock but next pass is able to get access to locked stream:
My implementation:
Implementation of RedisLock the same as in article.
public async void ListenTask()
{
var handledResult = await db.StreamRangeAsync(streamName, "-", "+", 1, Order.Descending);
var lowestHandledId = handledResult.Last().Id;
var readTask = Task.Run(async () =>
{
while (!Token.IsCancellationRequested)
{
var result = await db.StreamRangeAsync(streamName, lowestHandledId, "+", 2);
var handleResult = result.Last();
if (result.Any() && lowestHandledId != handleResult.Id)
{
bool isLocked = RedisLock.AcquireLock(streamName, handleResult.Id.ToString(), expiry);
if (!isLocked)
{
//lock
lowestHandledId = handleResult.Id;
var streamCat = handleResult.Values;
Cat cat = ParseResult(streamCat);
switch (streamCat[0].Value.ToString())
{
case "insert":
Console.WriteLine($"Insert cat at id:{cat.Id} [{cat.Name} - {cat.CreatedDate}]");
cacheDictionary.Add(cat.Id, new WeakReference(cat));
break;
case "delete":
Console.WriteLine($"Deleted cat at id:{cat.Id}");
cacheDictionary.Remove(cat.Id);
break;
}
RedisLock.ReleaseLock(streamName, handleResult.Id.ToString());
}
}
await Task.Delay(2000);
}
});
}
I solve my problems with RedisLock implemetation by using LockTake/LockRelease commands.
By following this article:
stackoverflow question
public async void ListenTask()
{
var handledResult = await db.StreamRangeAsync(streamName, "-", "+", 1, Order.Descending);
var lowestHandledId = handledResult.Last().Id;
RedisValue token = Environment.MachineName;
var readTask = Task.Run(async () =>
{
while (!Token.IsCancellationRequested)
{
var result = await db.StreamRangeAsync(streamName, lowestHandledId, "+", 2);
var handleResult = result.Last();
if (result.Any() && lowestHandledId != handleResult.Id)
{
if (!db.LockTake(streamName, token, expiry))
{
lowestHandledId = handleResult.Id;
var streamCat = handleResult.Values;
Cat cat = ParseResult(streamCat);
switch (streamCat[0].Value.ToString())
{
case "insert":
Console.WriteLine($"Insert cat at id:{cat.Id} [{cat.Name} - {cat.CreatedDate}]");
cacheDictionary.Add(cat.Id, new WeakReference(cat));
break;
case "delete":
Console.WriteLine($"Deleted cat at id:{cat.Id}");
cacheDictionary.Remove(cat.Id);
break;
}
db.LockRelease(streamName, token);
}
}
await Task.Delay(100);
}
});
}
Related
This is my RestClientService :
public class RestClientService : IRestClientService
{
protected readonly RestClient _restClient;
protected readonly RestRequest _restRequest;
public RestClientService()
{
_restClient = new RestClient();
_restRequest = new RestRequest();
}
public async Task<MessageDTO> SendComment(Websites website, Comment comment, int postId)
{
if (await IsPostExist(postId,website))
{
_restClient.Options.BaseUrl = new Uri($"{website.Url}/wp-json/wp/v2/comments/");
_restRequest.Method = Method.Post;
_restRequest.RequestFormat = DataFormat.Json;
_restRequest.AddJsonBody(new
{
post = postId,
author_name = comment.Author,
content = comment.Body
});
var result = await _restClient.ExecuteAsync(_restRequest);
return new MessageDTO
{
Message = result.Content,
Status = result.StatusCode.ToString()
};
}
return new MessageDTO
{
Message = "Post Not Found",
Status = "404"
};
}
}
I have a list of comments and list of products that I iterate over them and call SendComment method from RestClientService class. The problem is in first time SendComment method called _restRequest object will be get JsonBody and everything is okay but next time this method calls in loop _restRequest object has old data and won't be renewed.
In DI Container I added (Transient) RestClientService
builder.Services.AddTransient<IRestClientService,RestClientService>();
Here is where I used SendComment method in another service.
public async Task<MessageDTO> CreateSendCommentJob(SendCommentConfiguration config)
{
var updatedConfig = await _sendCommentConfigurationRepository.Get(config.Id);
var ConfigDetails = JsonConvert.DeserializeObject<SendCommentConfigurationDetailsDTO>(updatedConfig.Configuration);
var mappedWebsite = _mapper.Map<Websites>(ConfigDetails.WebsiteInfo);
if (ConfigDetails.CommentType == "blog")
{
var comments = await _uCommentService.GetCommentsByGroupId(ConfigDetails.CommentGroupId);
var commentCount = 0;
for (int postCount = 0; postCount < ConfigDetails.ProductPerSendCount; postCount++)
{
var postId = ConfigDetails.Ids[postCount];
while (commentCount < ConfigDetails.CommentsPerProductCount)
{
var random = new Random();
var index = random.Next(comments.Count);
var result = await _restClientService.SendComment(mappedWebsite, comments[index], postId);
if (result.Status == "Created")
{
Console.WriteLine($"Comment Index ({index}) at Id ({postId}) submited successfuly...");
commentCount++;
}
else
{
Console.WriteLine($"{postId} - {result.Status} - {result.Message}");
}
}
ConfigDetails.Ids.Remove(postId);
commentCount = 0;
}
}
var newConfig = new SendCommentConfiguration
{
Id = config.Id,
Configuration = JsonConvert.SerializeObject(ConfigDetails)
};
await _sendCommentConfigurationRepository.Edit(newConfig);
return new MessageDTO
{
Status = "200",
Message = "Comments Successfuly Sent "
};
}
_restClientService.SendComment is called in a loop on the same service instance. As the request instance is a field of the service instance, the same request instance is reused for each call.
As each apple performs a distinct request, each request must use a distinct instance of RestRequest, like :
public class RestClientService : IRestClientService
{
public async Task<MessageDTO> SendComment(Websites website, Comment comment, int postId)
{
if (await IsPostExist(postId,website))
{
//Each call, new instances
var restClient = new RestClient();
var restRequest = new RestRequest();
restClient.Options.BaseUrl = new Uri($"{website.Url}/wp-json/wp/v2/comments/");
restRequest.Method = Method.Post;
restRequest.RequestFormat = DataFormat.Json;
restRequest.AddJsonBody(new
{
post = postId,
author_name = comment.Author,
content = comment.Body
});
var result = await restClient.ExecuteAsync(_restRequest);
return new MessageDTO
{
Message = result.Content,
Status = result.StatusCode.ToString()
};
}
return new MessageDTO
{
Message = "Post Not Found",
Status = "404"
};
}
}
Hi guys i am trying write firestore.instance but it give error. I can not under stand why this error occur in the below code.
void _fetchMarkersFromDb() {
// TODO: improve this
print('_fetchMarkersFromDb() called');
***Firestore***.instance.collection('markers').getDocuments().then((docs) async {
final docLength = docs.documents.length;
final clients = List(docLength);
for (int i = 0; i < docLength; i++) {
clients[i] = docs.documents[i];
}
if (!isFirstCycle && isMyMarkerFetched) {
currentLocation = await Geolocator.getCurrentPosition();
}
_populateMarkers(clients);
});
}
enter image description here
Use FirebaseFirestore instead.
Like:
FirebaseFirestore.instance.collection('markers').get().then((value) async {
var docs = value.docs;
final docLength = docs.length;
final clients = List(docLength);
for (int i = 0; i < docLength; i++) {
clients[i] = docs[i];
}
if (!isFirstCycle && isMyMarkerFetched) {
currentLocation = await Geolocator.getCurrentPosition();
}
_populateMarkers(clients);
});
To delete a document use:
FirebaseFirestore.instance.collection('markers').doc(documentId).delete()
I have a mobile application based on Xamarin and a Web API based on .Net Core. Mobile app consumes methods of Web API via HttpClient. The code below is my base method to call any Web API method and the point is I want to set a timeout but could not achieved to set the exact timeout value whatever I have implemented. Tried Timespan.FromSeconds() or TimeSpan.FromMilliseconds() etc. When client makes a request to Web API, a loader is displayed to lock UI and removed after API response. Some clients gave me a feedback that the loader is displayed forever and request never ends. Maybe, the server is unreachable in this particular time or internet connection is broken for client etc. All I want to set a timeout and break the request and display an alert message to client. Of course, I googled and tried too much as mentioned but no result. If anyone can help me, will be appreciated.
public async Task<BaseResponseModel> Post(BasePostModel postModel)
{
var responseModel = new BaseResponseModel();
var json = postModel.ToString();
var jsonParam = new StringContent(json, Encoding.UTF8, "application/json");
var isPosted = true;
var clientHandler = new HttpClientHandler()
{
AllowAutoRedirect = true,
};
var url = GetURL(postModel.UrlKey);
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
ContractResolver = new DefaultContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var client = new HttpClient(clientHandler);
//client.Timeout = TimeSpan.FromSeconds(10);
//var cancellationTokenSource = new CancellationTokenSource();
//cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("X-Env", "MOBILE_API");
AttachToken(ref client, responseModel.Id);
try
{
if (Preferences.ContainsKey("UserJwtExprieDate"))
{
var expiryDate = Preferences.Get("UserJwtExprieDate", null);
if (DateTime.Now > DateTime.Parse(expiryDate))
{
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
int index = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count - 1;
Page currPage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack[index];
if (currPage as SigninForFactorOne != null)
{}
else
{
App.LogoutUser();
}
}
else
{
var response = await client.PostAsync(url, jsonParam);
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings);
if (resultModel.ErrorType == APIErrorTypes.NULL)
{
if (resultModel.IsSucceed)
{
responseModel.Data = resultModel.Data;
}
else
{
responseModel.Error = resultModel.Error;
}
responseModel.Message = resultModel.Message;
}
else
{
responseModel.Error = "Token Expried Date.";
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
App.LogoutUser();
}
}
else
{
new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
isPosted = false;
}
}
}
else
{
var response = await client.PostAsync(url, jsonParam);
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings);
if (resultModel.ErrorType == APIErrorTypes.NULL)
{
if (resultModel.IsSucceed)
{
responseModel.Data = resultModel.Data;
}
else
{
responseModel.Error = resultModel.Error;
}
responseModel.Message = resultModel.Message;
}
else
{
responseModel.Error = "Token Expried Date.";
Preferences.Remove("UserJwtExprieDate");
Preferences.Remove("HomePageInformation");
App.LogoutUser();
}
}
else
{
new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
isPosted = false;
}
}
}
catch (Exception ex)
{
new AppException(ex, responseModel.Id, 500, "anonymous.user", "Unable to post data to API!");
isPosted = false;
}
finally
{
if (!isPosted)
{
responseModel.Error = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
responseModel.Message = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
}
}
return responseModel;
}
I've used the solution below to manually set a time-out which works fine.
internal class TimeOutHandler : DelegatingHandler
{
private readonly TimeSpan TimeOut;
public TimeOutHandler(TimeSpan timeOut) => TimeOut = timeOut;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage req, CancellationToken ct)
{
using (var ctTimeOut = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
ctTimeOut.CancelAfter(TimeOut);
try
{
return await base.SendAsync(req, ctTimeOut.Token);
}
catch (OperationCanceledException) when (!ct.IsCancellationRequested)
{
throw new TimeoutException();
}
}
}
}
How to use
var interval = TimeSpan.FromSeconds(10);
var handler = new TimeOutHandler(interval)
{
InnerHandler = new HttpClientHandler()
};
var client = new HttpClient(handler);
For more information, check out: https://thomaslevesque.com/2018/02/25/better-timeout-handling-with-httpclient/
I have this code where am supposed to upload an image and get the downloaded url but whenever i do that I get this error
my url is String url;. So please why is this not working as it is supposed to
PS
I checked other website to learn how to properly upload but it keeps giving me an error or is there a better way to do this.
My code image
uploadTask.whenComplete(()async{
url = await refs.getDownLoadURL();
....
});
Since it returns a Future you need to wait for it to be accessed
Example :
Future<String> createFolder(String folderName) async {
final dir = Directory(
'${(io.Platform.isAndroid ? await getExternalStorageDirectory() //FOR ANDROID
: await getApplicationSupportDirectory() //FOR IOS
)!.path}/$folderName');
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if ((await dir.exists())) {
return dir.path;
} else {
dir.create();
return dir.path;
}
}
Future<String> getIslamiSahittoBookFilePath(String savename) async {
Future<String> s = createFolder("Islami_Sahitto");
String filePath = await s;
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
//add more permission to request here.
].request();
io.File? f = null;
if (statuses[Permission.storage]!.isGranted) {
Directory? dir = await DownloadsPath.downloadsDirectory();
if (dir != null) {
String savePath = "${dir.path}/$filePath/$savename";
f = new io.File(savePath);
if (await f.exists()) {}
}
}
return f.toString();
}
Now this block You can use AnyWhere : Future String, to String :
bool isPreviousDownloaded = false;
String previousFilePath = "null";
getIslamiSahittoBookFilePath(fileNameToDownload).then((value) {
if (value != null) {
setState(() {
isPreviousDownloaded = true;
previousFilePath = value;
});
}
});
I am trying to query orders and update them. I have been able to isolate my problem in a unit test:
[Fact(DisplayName = "OrderDocumentRepositoryFixture.Can_UpdateAsync")]
public async void Can_UpdateByQueryableAsync()
{
var order1 = JsonConvert.DeserializeObject<Order>(Order_V20170405_133926_9934934.JSON);
var orderId1 = "Test_1";
order1.Id = orderId1;
await sut.CreateAsync(order1);
foreach (var order in sut.CreateQuery())
{
order.Version = "myversion";
await sut.UpdateAsync(order);
var ordersFromDb = sut.GetByIdAsync(orderId1).Result;
Assert.Equal("myversion", ordersFromDb.Version);
}
}
where :
public IQueryable<T> CreateQuery()
{
return _client.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(_databaseId, CollectionId));
}
With this code, orders are not updated.
If I replace the CreateQuery() by what follows, it does work:
[Fact(DisplayName = "OrderDocumentRepositoryFixture.Can_UpdateAsync")]
public async void Can_UpdateByQueryableAsync()
{
var order1 = JsonConvert.DeserializeObject<Order>(Order_V20170405_133926_9934934.JSON);
var orderId1 = "Test_1";
order1.Id = orderId1;
await sut.CreateAsync(order1);
var order = sut.GetByIdAsync(orderId1).Result;
order.Version = "myversion";
await sut.UpdateAsync(order);
var ordersFromDb = sut.GetByIdAsync(orderId1).Result;
Assert.Equal("myversion", ordersFromDb.Version);
}
where
public async Task<T> GetByIdAsync(string id)
{
try
{
var documentUri = UriFactory.CreateDocumentUri(_databaseId, CollectionId, id);
var document = (T) await ((DocumentClient) _client).ReadDocumentAsync<T>(documentUri);
return document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == HttpStatusCode.NotFound) return null;
throw;
}
}
I've been trying to understand why this doesn't work. Obviously i could always do a GetByIdAsync before updating, but that seems overkill?
What can't I see?
Thanks!
You create your query, but you never execute it (CreateDocumentQuery just sets up the query). Try altering your call to something like:
foreach (var order in sut.CreateQuery().ToList())
{
//
}
Also note: if you are always querying for a single document, and you know the id, then ReadDocumentAsync() (your alternate code path) will be much more effecient, RU-wise.