We have a Silverlight application that calls a web service to retrieve database rows from SQL Server. These are then displayed on the screen in a paged control. However, the entire table is needed and it consists of several thousand rows. On the customer system, if we ask for more than around 1500 rows, we get HttpRequestTimedOutWithoutDetail. On our development system we need about 500,000 rows before this happens.
Obviously, what we should be doing is paging the results and returning them to the silverlight bit by bit. But I don't know how to do this. Can anyone advise, or point me to some web-pages that clearly explain the principles and methods (I am a bit simple!)
Here is the code in the Web Service:
public IQueryable<Referral> GetReferrals()
{
/*
* In the customer's environments it seems that any more than around 1500 referrals will break the system: they will fail to load.
* In the dev environment is takes around 500,000 so it seems to be timeout related.
*
* The code below was an attempt to restrict the number, only returning referrals above a certain size and within a certain age.
* It seems the customer is more interested in the smaller referrals though since they are more likely to be added to existing
* substations so if this method is re-instated, we should be using '<' instead of '>'
*
int mdToBeMet = int.Parse(ConfigurationManager.AppSettings["ReferralMDToBeMet"]);
DateTime minusNYears = DateTime.Today.AddYears(int.Parse(ConfigurationManager.AppSettings["ReferralTargetDate"]) * -1);
int maxReferralsCount = int.Parse(ConfigurationManager.AppSettings["ReferralMaxRecordCount"]);
if (mdToBeMet != 0 && maxReferralsCount != 0)
{
return this.ObjectContext.Referrals.Where(x => x.MD_to_be_Met > mdToBeMet && x.Target_Date > minusNYears).OrderByDescending(y => y.Target_Date).Take(maxReferralsCount);
}
*/
/*
* This is the 2nd attempt: the customer is mainly interested in referrals that have an allocated substation
*/
bool allocatedReferralsOnly = bool.Parse(ConfigurationManager.AppSettings["ReferralAllocatedOnly"]);
int maxReferralsCount = int.Parse(ConfigurationManager.AppSettings["ReferralMaxRecordCount"]);
if (allocatedReferralsOnly)
{
var referrals = this.ObjectContext.Referrals.Where(x => x.Sub_no != "").OrderByDescending(y => y.Target_Date).Take(maxReferralsCount);
return referrals;
}
else
{
/*
* Ideally, we should just page the referrals here and return to retrieving all of them, bit by bit.
*/
var referrals = this.ObjectContext.Referrals;
return referrals;
}
}
Many thanks for any suggestions.
An example to expand on the comment I gave ...
/// <summary>
/// Returns a page of Referrels
/// pageNumber is 0-index based
/// </summary>
public IQueryable<Referrel> GetReferrals(int pageNumber)
{
var pageSize = 100;
return ObjectContext.Referrals.Skip(pageNumber*pageSize).Take(pageSize);
}
Obviously you could pass in the pageSize too if you wanted, or define it as a constant somewhere outside of this method.
Related
I have an List items to be inserted into the DynamoDb collection. The size of the list may vary from 100 to 10k. I looking for an optimised way to Batch Write all the items using the BatchWriteItemEnhancedRequest (JAVA SDK2). What is the best way to add the items into the WriteBatch builder and then write the request using BatchWriteItemEnhancedRequest?
My Current Code:
WriteBatch.Builder<T> builder = BatchWriteItemEnhancedRequest.builder().writeBatches(builder.build()).build();
items.forEach(item -> { builder.addPutItem(item); });
BatchWriteItemEnhancedRequest bwr = BatchWriteItemEnhancedRequest.builder().writeBatches(builder.build()).build()
BatchWriteResult batchWriteResult =
DynamoDB.enhancedClient().batchWriteItem(getBatchWriteItemEnhancedRequest(builder));
do {
// Check for unprocessed keys which could happen if you exceed
// provisioned throughput
List<T> unprocessedItems = batchWriteResult.unprocessedPutItemsForTable(getTable());
if (unprocessedItems.size() != 0) {
unprocessedItems.forEach(unprocessedItem -> {
builder.addPutItem(unprocessedItem);
});
batchWriteResult = DynamoDB.enhancedClient().batchWriteItem(getBatchWriteItemEnhancedRequest(builder));
}
} while (batchWriteResult.unprocessedPutItemsForTable(getTable()).size() > 0);
Looking for a batching logic and a more better way to execute the BatchWriteItemEnhancedRequest.
I came up with a utility class to deal with that. Their batches of batches approach in v2 is overly complex for most use cases, especially when we're still limited to 25 items overall.
public class DynamoDbUtil {
private static final int MAX_DYNAMODB_BATCH_SIZE = 25; // AWS blows chunks if you try to include more than 25 items in a batch or sub-batch
/**
* Writes the list of items to the specified DynamoDB table.
*/
public static <T> void batchWrite(Class<T> itemType, List<T> items, DynamoDbEnhancedClient client, DynamoDbTable<T> table) {
Stream<List<T>> chunksOfItems = Lists.partition(items, MAX_DYNAMODB_BATCH_SIZE);
chunksOfItems.forEach(chunkOfItems -> {
List<T> unprocessedItems = batchWriteImpl(itemType, chunkOfItems, client, table);
while (!unprocessedItems.isEmpty()) {
// some failed (provisioning problems, etc.), so write those again
unprocessedItems = batchWriteImpl(itemType, unprocessedItems, client, table);
}
});
}
/**
* Writes a single batch of (at most) 25 items to DynamoDB.
* Note that the overall limit of items in a batch is 25, so you can't have nested batches
* of 25 each that would exceed that overall limit.
*
* #return those items that couldn't be written due to provisioning issues, etc., but were otherwise valid
*/
private static <T> List<T> batchWriteImpl(Class<T> itemType, List<T> chunkOfItems, DynamoDbEnhancedClient client, DynamoDbTable<T> table) {
WriteBatch.Builder<T> subBatchBuilder = WriteBatch.builder(itemType).mappedTableResource(table);
chunkOfItems.forEach(subBatchBuilder::addPutItem);
BatchWriteItemEnhancedRequest.Builder overallBatchBuilder = BatchWriteItemEnhancedRequest.builder();
overallBatchBuilder.addWriteBatch(subBatchBuilder.build());
return client.batchWriteItem(overallBatchBuilder.build()).unprocessedPutItemsForTable(table);
}
}
My question is about a strange behavious I notice both on my iPhone device and the codenameone simulator (NetBeans).
I invoke the following code below which calls a google web service to provide a list of food places around a GPS coordinate:
The web service that is called is as follows (KEY OBSCURED):
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXXXXXXXXXXXXXXXXX
Each result contains the next page token and thus, the second call (for the subsequent page) is as follows:
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXXXXXXXXXXXXXXXXX&pagetoken=YYYYYYYYYYYYYYYYYY
public static byte[] getWSResponseData(String urlString, boolean usePost)
{
ConnectionRequest r = new ConnectionRequest();
r.setUrl(urlString);
r.setPost(usePost);
InfiniteProgress prog = new InfiniteProgress();
Dialog dlg = prog.showInifiniteBlocking();
r.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueueAndWait(r);
try
{
Thread.sleep(2000);
}
catch (InterruptedException ex)
{
}
byte[] responseData = r.getResponseData();
return responseData;
}
public static void getLocationsList(double lat, double lng)
{
boolean done = false;
while (!done)
{
byte[] responseData = getWSResponseData(finalURL,false);
result = Result.fromContent(parser.parseJSON(new InputStreamReader(new ByteArrayInputStream(responseData))));
String venueNames[] = result.getAsStringArray("/results/name");
nextToken = result.getAsString("/next_page_token");
if ( nextToken == null || nextToken.equals(""))
done = true;
else
finalURL = completeURL + "&pagetoken=" + nextToken;
}
.....
}
This code works fine with the sleep timer, but when I remove the Thread.sleep, only the first page gets called.
Any help would be appreciated.
Using the debugger does not help as this is a timing issue and the issue does not occur when using the debugger.
Also when I put some print statements into the code
while (!done)
{
String nextToken = null;
**System.out.println(finalURL);**
...
}
System.out.println("Total Number of entries returned: " + itemCount);
I get the following output:
First Run (WITHOUT SLEEP):
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXX
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXX&pagetoken=CqQCF...
Total Number of entries returned: 20
Using the network monitor I see that the response to the second WS call returns:
{
"html_attributions" : [],
"results" : [],
"status" : "INVALID_REQUEST"
}
Which is strange as when I cut and paste the WS URL into my browser, it works fine...
Second Run (WITH SLEEP):
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXXX
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXXX&pagetoken=CqQCFQEAA...
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.714353,-74.00597299999998&radius=200&types=food&key=XXXXXXXXX&pagetoken=CsQDtQEAA...
Total Number of entries returned: 60
Well it seems to be a google API issue as indicated here:
Paging on Google Places API returns status INVALID_REQUEST
I still could not get it to work by changing the WS URL with a random parameter as they suggested, but I will keep trying and post something here if I get it to work. For now I will just keep a 2 second delay between the calls which seems to work.
Well gave up on using the google WS for this and switched to Yelp, works very well:
https://api.yelp.com/v3/businesses/search?.....
I'm working on upgrading from 8.1 to 8.2 update 3. I understand that the search/indexing was changed and some code is obsolete or no longer works.
Unfortunately in one of our class projects there were some methods our sitecore partners had built for us that are no longer working due to obsolete code and now I need help updating that code. I've been able to narrow it down to just 2 files, with some simple code.
Here is the first set of code:
public partial class TagResults
{
/// <summary>
/// Searches against the Lucene index for pages given a specific tag
/// </summary>
/// <returns></returns>
public List<BasePage> GetPagesForTag(string Tag, int page, int pageSize, out int totalCount)
{
var tags = new List<BasePage>();
Index searchIndex = SearchManager.GetIndex(Constants.LuceneIndexes.Tags);
using (IndexSearchContext context = searchIndex.CreateSearchContext())
{
//The wildcard search allows us to pull back all items with the given tag
var query = new WildcardQuery(new Term(Constants.LuceneFields.Tags, Tag));
SearchHits hits = context.Search(query);
totalCount = hits.Length;
//Go through the results
SearchResultCollection results = hits.FetchResults(page * pageSize, pageSize);
foreach (SearchResult result in results)
{
var searchItem = result.GetObject<Item>();
Item currentDbItem = ItemUtility.GetItem(searchItem.ID);
//Store the item if it exists and is a descendant of the news listing
if (currentDbItem != null)
{
tags.Add(new BasePage(currentDbItem));
}
}
}
return tags.ToList();
}
public static bool IsItemCastable(Item sitecoreItem)
{
return sitecoreItem != null && !string.IsNullOrEmpty(sitecoreItem[FieldIds.IsTagResultsComponent]);
}
}
I think I have some of this changed correctly:
ISearchIndex searchIndex = ContentSearchManager.GetIndex(Constants.LuceneIndexes.tags);
using (IProviderSearchContext context = searchIndex.CreateSearchContent())
But then I get stuck on
SearchHits hits = context.Search(query);
totalCount = hits.Length;
//Go through the results
SearchResultCollection results = hits.FetchResults(page * pageSize, pageSize);
foreach (SearchResult result in results)
Now I'm asking a lot but I am learning. If someone could help me correct this, I would be really appreciative. I've been doing a lot of searching to try to figure out how to correct this but I feel like I'm missing something and I just need some help.
Thank you in advance.
I'am new to Apex and I have to call a webservice for every account (for some thousands of accounts).
Usualy a single webservice request takes 500 to 5000 ms.
As far as I know schedulable and batchable classes are required for this task.
My idea was to group the accounts by country codes (Europe only) and start a batch for every group.
First batch is started by the schedulable class, next ones start in batch finish method:
global class AccValidator implements Database.Batchable<sObject>, Database.AllowsCallouts {
private List<String> countryCodes;
private countryIndex;
global AccValidator(List<String> countryCodes, Integer countryIndex) {
this.countryCodes = countryCodes;
this.countryIndex = countryIndex;
...
}
// Get Accounts for current country code
global Database.QueryLocator start(Database.BatchableContext bc) {...}
global void execute(Database.BatchableContext bc, list<Account> myAccounts) {
for (Integer i = 0; i < this.AccAccounts.size(); i++) {
// Callout for every Account
HttpRequest request ...
Http http = new Http();
HttpResponse response = http.send(request);
...
}
}
global void finish(Database.BatchableContext BC) {
if (this.countryIndex < this.countryCodes.size() - 1) {
// start next batch
Database.executeBatch(new AccValidator(this.countryCodes, this.countryIndex + 1), 200);
}
}
global static List<String> getCountryCodes() {...}
}
And my schedule class:
global class AccValidatorSchedule implements Schedulable {
global void execute(SchedulableContext sc) {
List<String> countryCodes = AccValidator.getCountryCodes();
Id AccAddressID = Database.executeBatch(new AccValidator(countryCodes, 0), 200);
}
}
Now I'am stuck with Salesforces execution governors and limits:
For nearly all callouts I get the exceptions "Read timed out" or "Exceeded maximum time allotted for callout (120000 ms)".
I also tried asynchronous callouts, but they don't work with batches.
So, is there any way to schedule a large number of callouts?
Have you tried to limit your execute method to 100? Salesforce only allows 100 callout per transaction. I.e.
Id AccAddressID = Database.executeBatch(new AccValidator(countryCodes, 0), 100);
Perhaps this might help you:
https://salesforce.stackexchange.com/questions/131448/fatal-errorsystem-limitexception-too-many-callouts-101
What is the most efficient way of reading in lots of images in CF / Railo and checking their width and height?
In my app, I need to typically read in about 20 images + and at the moment this takes up to 14 seconds to complete. A bit too long really.
theImageRead = ImageNew(theImageSrc);
if ( imageGetWidth(theImageRead) > 100 ) {
writeOutput('<img src="' & theImageSrc & '" />');
}
Images are read from a list of absolute URL's. I need to get images specified over a certain dimension.
If there's a quicker solution to this then I'd love to get your insight. Perhaps underlying java methods?
I am also using jSoup if there's anything in that which could help.
Thanks,
Michael.
I don't believe there's any way to determine the pixel dimensions of an image without reading the bytes and creating an image object. The main bottleneck here will be the http request overhead.
that said there are a few ways to speed up what you're trying to do.
use threads to concurrently request images, then when all threads have finished processing output the images.
If you display the same image or set of images more than once cache it. If you don't want to cache the actually image you can cache the metadata to avoid having to perform a http request for every image.
decide if you need to output all the images to the page immediately, or could some or all of these be deferred and loaded via and ajax request
I have written this utility function quite a while ago (it runs on older ColdFusion versions, too). Maybe it helps.
Note that this requires the Java Advanced Imaging Image I/O Tools (Jai-imageio). Download the .jar and put it in your class path (restarting CF is necessary).
/**
* Reads basic properties of many types of images. Values are
* returned as a struct consisting of the following elements:
*
* Property names, their types and default values:
* ImgInfo.width = 0 (pixels)
* ImgInfo.height = 0 (pixels)
* ImgInfo.size = 0 (bytes)
* ImgInfo.isGrayscale = false (boolean)
* ImgInfo.isFile = false (boolean)
* ImgInfo.success = false (boolean)
* ImgInfo.error = "" (string)
*
* #param FilePath Physical path to image file.
* #return A struct, as described.
*/
function GetImageProperties(FilePath) {
var ImgInfo = StructNew();
var jImageIO = CreateObject("java", "javax.imageio.ImageIO");
var jFile = CreateObject("java", "java.io.File").init(FilePath);
var jBufferedImage = 0;
var jColorSpace = 0;
ImgInfo.width = "";
ImgInfo.height = "";
ImgInfo.fileSize = 0;
ImgInfo.isGrayscale = false;
ImgInfo.isFile = jFile.isFile();
ImgInfo.success = false;
ImgInfo.error = "";
try {
jBufferedImage = jImageIO.read(jFile);
ImgInfo.fileSize = jFile.length();
ImgInfo.width = jBufferedImage.getWidth();
ImgInfo.height = jBufferedImage.getHeight();
jColorSpace = jBufferedImage.getColorModel().getColorSpace();
ImgInfo.isGrayscale = (jColorSpace.getType() eq jColorSpace.TYPE_GRAY);
ImgInfo.success = true;
}
catch (any ex) {
ImgInfo.error = ToString(ex);
}
jImageIO = JavaCast("null", "");
jFile = JavaCast("null", "");
jBufferedImage = JavaCast("null", "");
jColorSpace = JavaCast("null", "");
return ImgInfo;
}
Use like:
imageInfo = GetImageProperties(theImageSrc);
if (imageInfo.success and imageInfo.width > 100)
writeOutput('<img src="#HTMLEditFormat(theImageSrc)#" />');
}