Running a Meteor.call many times in a row is slow

I have a Meteor.call running in a useEffect. I actually have the src of the image passed down to an Image component where the useEffect lives, so I am able to quickly render the images on the page. The Image component is used all through my code base but for example it could run in a map.

Post collection data document

dataPostDocument.posts.map((post)=> {
 return (
   <Image src={post.image.src}/>
  )
}
const Image = ({src}) => {
useEffect(() => {
		if (src) {
			setImage({ src: src });
                        // Image collection
			Meteor.call('findImageNoCategorizationFull', id, (err, res) => {
				if (res) {
					console.log("result is this: ", res)
					setImage((prev) => ({ ...prev, alt_text: res.alt_text }));
				}
				if (err) {
				}
			})
		}
	}, [src]);
}

Now where my issue starts is I have a post collection and a images collection. The post collection references the Image collection to render the image by the image_id stored in the post. The image collection document has the src_path and the altText, now the post collection also has the src path which is a fragment of an older implementation, really we want to reference the image src, by looking it up from the posts document image_id. This does work.

However it is very slow, the alt_text doesn’t show up for like a minute to do the whole page. All the images show up immediately because I am pulling there src from the post document which is the old pattern. If I were to remove the setImage({src:src}) and move it into the Meteor.call with {src:res.src} it would work but the images take forever to load.

The meteor method looks like this

findImageNoCategorizationFull(id) {

		console.log(id);

		const file = Images.findOne(id);

		console.log("file is:", file)

		const image = {
			_id: file._id,
			id: file._id,
			categories: file.categories,
			src: `cdn/storage/${file._collectionName}/${file._id}/original/${file._id}${file.extensionWithDot}`,
			alt_text: file.alt_text,
		};

		return image;
	},

I think it is the multiple calls of the same method causing this issue.

Any help would be greatly appreciated.

check out the docs for this.unblock(). Might help.

2 Likes

To check why it is slow, you can use montiapm:debug. It shows what is happening on the client and server so you can see the cause. If it is caused by the methods blocking as @generalledger suggested, which seems likely, it will show up in montiapm:debug as a large amount of wait time.

1 Like

Thank you so much for the replies, I will look into these suggestions

The other option would be to denormalize or “join” the image data with the post when fetching post details, that way the client will have the data in fewer fetches

But my guess is that unblock will help a lot

Is there any way that I still have wait time while I have a this.unblock inside my methods ?

To give more infos, here is an APM screenshot

the comment.getNumber method is a ValidatedMethod which has the unblock mixin that I have define like this

export default options => {
  const { run } = options;

  return Object.assign(options, {
    run(...args) {
      if (this.unblock && typeof this.unblock === "function") {
        this.unblock();
      }
      return run.call(this, ...args);
    },
  });
};

What can cause such a wait time ?

Have you tried sticking this.unblock() directly in the run property when you define your ValidatedMethod? I don’t see anything wrong with your mixin but it’s always good to double-check with such “magic” things.

Alternatively, you could stick async in front of run, which unblocks the method as well.

I did without luck, any thoughts @zodern ?

For cases like this, where you have a list of items and need some data on each item, there are a couple of options.

  1. Include that data in the document in the first place
  2. Have the item data fetching happen in a method, and use an aggregate to include the data
  3. If you use a subscription to get the items, have one single method that fetches all the data you are missing, and save it to state. Then do a diff on later changes and only fetch the updated data. So you would have a method that takes a list of IDs to fetch, and return a list of the data you need, then you associate that with the subscribed items. Way faster to batch the data in one go like this, and you only need to do one DB query on the server for the initial load, instead of N small queries and methods.