andygunther | 2016-11-03 15:50:34 UTC | #1
Using the .Net SDK, I'm working through a process to pull ALL calls for a given interval so that I can extract the segments and write out some data about them.
With IDENTICAL inputs, I get varying results from one execution to the next. In the last four executions of the same interval, I've seen the API return null results between pages 2 & 3 twice, and different totals when reaching the last page twice.
Is there a way to get a better understanding why I'm getting such inconsistent results?
This is a snip of my code:
bool bMorePages = true; int iPageIndex = 1;
ININ.PureCloudApi.Api.ConversationsApi oConvoApi = new ININ.PureCloudApi.Api.ConversationsApi(); oConvoApi.Configuration.AccessToken = m_sToken; string interval = dtpStart.Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff") + "/" + dtpEnd.Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff");
ININ.PureCloudApi.Model.ConversationQuery body = new ININ.PureCloudApi.Model.ConversationQuery();
body.Interval = interval; body.Paging = new ININ.PureCloudApi.Model.PagingSpec(100, iPageIndex);
ININ.PureCloudApi.Model.AnalyticsConversationQueryResponse results; ININ.PureCloudApi.Model.AnalyticsConversation oConvo;
while (bMorePages) { body.Paging.PageNumber = iPageIndex; System.Threading.Thread.Sleep(10000); //I added this because I thought the transition from one page to another might be the problem - it didn't have any impact on consistency results = GetConversations(body, oConvoApi); if (results != null) { if (results.Conversations != null && results.Conversations.Count > 0) { foreach (ININ.PureCloudApi.Model.AnalyticsConversation oConversation in results.Conversations) { oConvo = GetConversationDetails(oConversation, oConvoApi); if (oConvo != null) { foreach (ININ.PureCloudApi.Model.AnalyticsParticipant oParticipant in oConvo.Participants) { foreach (ININ.PureCloudApi.Model.AnalyticsSession oSession in oParticipant.Sessions) { foreach (ININ.PureCloudApi.Model.AnalyticsConversationSegment oSegment in oSession.Segments) { //deal with my segments } } } } } iPageIndex++; } else { bMorePages = false; } } else { bMorePages = false; } }
tim.smith | 2016-11-03 15:56:19 UTC | #2
Can you provide correlation IDs for the varying requests? I know that there are some things that can be "fuzzy", like the total count of records or pages because The API don't know exactly what exists until it retrieves the data and inspects it. Though I wouldn't expect actual data to disappear or change.
andygunther | 2016-11-03 15:59:42 UTC | #3
Where do I get the Correlation Id in the .Net SDK objects? I'm looking through, but don't see anything related to that off hand.
tim.smith | 2016-11-03 16:01:45 UTC | #4
If you use the WithHttpInfo variety of the API methods, you can retrieve the headers from the response object.
andygunther | 2016-11-03 16:03:08 UTC | #5
So, there's not way to troubleshoot this in the .Net SDK?
tim.smith | 2016-11-03 16:19:05 UTC | #6
You can use the SDK. Instead of using:
AnalyticsConversation result = analyticsApi.GetConversationsConversationIdDetails(...)
use
ApiResponse<AnalyticsConversation> result =
analyticsApi.GetConversationsConversationIdDetailsWithHttpInfo(...)
and get the correlation ID from result.Headers.
andygunther | 2016-11-03 16:45:25 UTC | #7
Do I just need the correlation id from the ConversationsApi object, or do I need to switch all of my logic around to use:
ApiResponse<ININ.PureCloudApi.Model.ConversationEntityListing><ININ.PureCloudApi.Model.ConversationEntityListing> response = oConvoApi.GetConversationsWithHttpInfo("Call");
Instead of:
ININ.PureCloudApi.Model.AnalyticsConversationQueryResponse results = GetConversations(body, oConvoApi);
andygunther | 2016-11-03 16:50:06 UTC | #8
I am concerned that what you are telling me here is that the only way to troubleshoot this is to rewrite most of what I've already written.
tim.smith | 2016-11-03 16:56:13 UTC | #9
If you don't want to change any code, you can always do a packet capture and grab the headers from there.
Do I just need the correlation id from the ConversationsApi object
The inin-correlation-id is in the response header, which is retrieved from the repsonse object, not the API object. In your GetConversations method, just change the API call to the WithHttpInfo method and return response.data instead of just response.
Additional question, does your interval contain active conversations (the current time)? If any conversations in the query interval change between repeated requests, the result set can change. This is the case even if none of the conversations in the results you got back changed.
andygunther | 2016-11-03 17:00:21 UTC | #10
My inconsistent results are pulling from the same interval each time, on 10/25. So, no active conversations.
The "WithHttpInfo" method doesn't appear to use the same strategy for querying/filtering. So, I need to go do a little reading on this.
Packet Capture would be nice, but since every thing I ask for has a Correlation Id, and it's all SSL, and we're pulling over 1000 calls (when it works), I don't see a reasonable way to use that method.
tim.smith | 2016-11-03 17:10:54 UTC | #11
andygunther, post:10, topic:571
The "WithHttpInfo" method doesn't appear to use the same strategy for querying/filtering. So, I need to go do a little reading on this.
Which resource are you using? PostConversationsDetailsQuery and PostConversationsDetailsQueryWithHttpInfo have the same parameter: ConversationQuery. They should behave identically except that the WithHttpInfo variety gives you back the response wrapped in an ApiResponse object.
andygunther | 2016-11-03 17:14:31 UTC | #12
Sorry - looking at the wrong method on my side... You're correct. Let me redo this test with the correlation id coming back for each page of the query.
andygunther | 2016-11-03 17:35:49 UTC | #13
Heading down this path exposed what I think is a mistake on my side. In the headers, I think you mentioned a while back that one of them would tell me how long I have to wait before making another API request. Is that inin-ratelimit-reset? Is that in seconds?
tim.smith | 2016-11-03 17:40:04 UTC | #14
andygunther, post:13, topic:571
how long I have to wait before making another API request. Is that inin-ratelimit-reset? Is that in seconds?
Kind of. That header is the number of seconds until your rate limit count is reset. You need to also look at the other two rate limiting headers to understand your standing with the API. You'll get 429 errors back if you're actively being rate limited. Here's the docs for API Rate Limiting.
Additionally, it is possible to be rate limited without the numbers in the headers being reached. Back end microservices, particularly analytics, will rate limit you if you're doing too much expensive querying. That back end rate limiting is not reflected in the header data, but will let you know because you got a 429 response.
flyte | 2016-11-04 17:49:37 UTC | #15
The whole purpose of using the SDK is to abstract the transport (in this case, HTTP) details, yet you have *WithHttpInfo methods.
I can't understand why you wouldn't put the API rate limiting details within the ApiException that is being thrown for the API call. I can't even get to the ApiResponse<CallConversation> to inspect the headers before the exception is thrown..
This makes it impossible to use in transient exception and fault handling libraries like Polly.
tim.smith | 2016-11-04 18:49:10 UTC | #16
flyte, post:15, topic:571
The whole purpose of using the SDK is to abstract the transport (in this case, HTTP) details, yet you have *WithHttpInfo methods.
The purpose of the SDK is to make development quicker and easier for you so you don't have to build all of the classes and helper methods yourself. The WithHttpInfo methods are there in case you need access to lower level constructs for debugging, troubleshooting, or any other purpose you find that you'd like to use data about the request/response itself. Without those methods, you'd be completely blind to any data sent in the headers, like the rate limiting information.
flyte, post:15, topic:571
I can't understand why you wouldn't put the API rate limiting details within the ApiException that is being thrown for the API call.
Are you not seeing the error body come through via ApiException.ErrorContent? ApiException.ErrorCode contains the 429 error code as well.
flyte, post:15, topic:571
I can't even get to the ApiResponse to inspect the headers before the exception is thrown
There is a pending issue, API-1830, to enhance the ApiResponse class itself as well as how it is used by the API classes. The goal is to make it more robust and behave in a similar manner as the Java SDK. One of those items is to optionally include the exception as a property of the ApiResponse object instead of throwing it. This will allow you to get full access to the headers and other information contained in ApiResponse even when there's an exception.
If you have any constructive feedback about the APIs or SDKs, please feel free to post information about what you'd like to see and what your use case is in the API Enhancements Request category.
tim.smith | 2016-11-04 21:39:27 UTC | #17
I've also created API-2065 to add the information in the rate limiting headers to the response body when a 429 response is sent.
system | 2017-08-28 19:28:23 UTC | #18
This post was migrated from the old Developer Forum.
ref: 571