Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

nachocodoner
Copy link
Member

@nachocodoner nachocodoner commented Mar 5, 2024

OSS-350

Context

Issue: #13036

A continuation of #12969, which provided a configuration to sort one of the problems on keeping the same behavior on running collection operations on Meteor 3.x in respect of Meteor 2.x. That fix resulted to be like a workaround, and hopefully with this new iteration is completely fixed.

Reproduction

Thanks to @bhunjadi I was able to understand and debug further the core flow about this issue and realized how Meteor 2.x and 3.x behaves distinctly in the process to run a collection operation.

In Meteor 2.x:

In Meteor 3.x:

It is depicted on the next attachment:

const NamedCollection = new Meteor.Collection('namedCollection');
    
await NamedCollection.insertAsync({ test: 1 });

console.log("-> isItemCreated?", NamedCollection.find().fetch()?.[0]);
setTimeout(() => {
    console.log("-> Later: isItemCreated?", NamedCollection.find().fetch()?.[0]);
}, 1_000);

image

This PR aims to ensure 3.x behavior matches the 2.x behavior. Data won't be persisted as the original github issue states (#13036), that is not the behavior on Meteor 2.x . For that, in Meteor, you need to fetch via subscription or a method, or directly use the local collection for inserting. Anyway, matching this behavior in both versions will also fix tests afftected by the community packages, since they expect the data to be available in the same run cycle that the op happened.

Fix

image

@nachocodoner nachocodoner added the Meteor 3 relates to Meteor 3 label Mar 5, 2024
@nachocodoner nachocodoner changed the title [3.0] Persist local doc in the same way Meteor 2.x [3.0] Create local doc when using collection operations in the same way Meteor 2.x Mar 5, 2024
@StorytellerCZ StorytellerCZ added this to the Release 3.0 milestone Mar 5, 2024
@harryadel
Copy link
Contributor

Hello @nachocodoner, just want to commend you on the careful and detailed explanation you provide in your PRs, along with the annotated screenshots. What a great way to share and spread knowledge! Please keep on doing it 👏 👏

@nachocodoner
Copy link
Member Author

nachocodoner commented Mar 6, 2024

@harryadel: Appreciate it! 😊

I try to do the best to comprehend the core and complexities of every component I engage with, consistently sharing my insights with the community in the form of text and images.

Regarding this complex issue, after thorough exploration and team discussions, it appears challenging. Considering the inherent async nature in Meteor 3.x, closing this PR might be necessary. Exploring alternatives like resolverType config, and employing stubPromise and serverPromise, seems imperative.

I'll provide a detailed explanation, ensuring the community is informed about this shift in approaching async methods and operations.

@nachocodoner
Copy link
Member Author

nachocodoner commented Mar 19, 2024

I'd like to clarify the differences observed between the behaviors of Meteor 2.x and 3.x versions through examples. The evolution in handling asynchronous operations in 3.x requires adjustments in client and server-side executions.

I'll illustrate this with practical scenarios, including ensuring code isomorphism, particularly in testing.

Given the following Meteor method and collection described on client and server.

const Greetings = new Mongo.Collection('greetings');

Meteor.methods({
  greetUser: function(name) {
    Greetings.insert({ doc: { userId: Meteor.userId(), name } });
    return "Hello, " + name + "!";
  }
});

Meteor 2.x

In Meteor 2.x, when calling the method with call:

Meteor.call('greetUser', 'John', function(error, result) {
  if (error) {
    console.error("Error:", error.reason); // 🔴 Server ended with error
  } else {
    console.log("Result:", result); // 🟢 Server ended with success
  }

  Greetings.findOne({ name: 'John' }); // 🗑️ Data is NOT available
});

// 🔵 Client simulation
Greetings.findOne({ name: 'John' }); // 🧾 Data is available (Optimistic-UI)

In this example above:

  • The Method runs on the server, while the client (🔵) acts on simulated data.
  • Upon Method completion, whether successful (🟢) or encountering an error (🔴), the client callback takes over. However, the data is no longer available in the collection and must be fetched via publication or manually inserted.

Meteor 3.x

In Meteor 3.x, when calling the method with callAsync, it returns stubPromise and serverPromise to manage client-side simulation and server response, respectively. By default, the server promise resolves as explained below.

serverPromise

Default / serverPromise (await)

try {
	await Meteor.callAsync('greetUser', 'John');
	// 🟢 Server ended with success
} catch(e) {
	console.error("Error:", error.reason); // 🔴 Server ended with error
}

Greetings.findOne({ name: 'John' }); // 🗑️ Data is NOT available

Default / serverPromise (then/catch)

await Meteor.callAsync('greetUser', 'John')
  .then(result => {
	console.log("Result:", result); // 🟢 Server ended with success
  })
  .catch(error => {
	console.error("Error:", error.reason); // 🔴 Server ended with error
  });

Greetings.findOne({ name: 'John' }); // 🗑️ Data is NOT available

In the examples above the client (🔵) simulation IS NOT handled and instead the server awaits for its result.

stubPromise

However, when handling stubPromise :

stubPromise (await)

await Meteor.callAsync('greetUser', 'John').stubPromise;

// 🔵 Client simulation
Greetings.findOne({ name: 'John' }); // 🧾 Data is available (Optimistic-UI)

This time, we only manage simulation time, not the server. Client errors can be handled with try/catch.

stubPromise and serverPromise

Another example using both promises.

const { stubPromise, serverPromise } = Meteor.callAsync('greetUser', 'John');

await stubPromise;

// 🔵 Client simulation
Greetings.findOne({ name: 'John' }); // 🧾 Data is available (Optimistic-UI)

try {
	await serverPromise;
	// 🟢 Server ended with success
} catch(e) {
	console.error("Error:", error.reason); // 🔴 Server ended with error
}

Greetings.findOne({ name: 'John' }); // 🗑️ Data is NOT available

In the example above, we could have handled any client error with try/catch as well.

This demonstrates the approach of async/await in Meteor 3.x and how it replicates previous callback and fibers-based behaviors.

resolverType

Additionally, to enforce the use of stubPromise by default, particularly useful on test environments to maintain isomorphic code, resolverType can be set as stub or server within the collection configuration. At the end running collection operations means executing a method when using remote collections.

const Greetings = new Meteor.Collection('greetUser', { resolverType: 'stub' });
    
await Greetings.insertAsync({ test: 1 });

// 🔵 Client simulation
Greetings.findOne({ name: 'John' }); // 🧾 Data is available (Optimistic-UI)

More details refer to the original PR on the addition of resolverType as a configuration.

Conclusion

We can't resolve the issue outlined in this PR because it aligns with the expected behavior of the new async approach using promises (3.x), which differs from when we utilized non-standard fibers (2.x).

I'll soon merge the PR with new tests covering the behaviors explained above and keep this post for future reference.

Copy link

netlify bot commented Mar 20, 2024

Deploy Preview for v3-meteor-api-docs ready!

Name Link
🔨 Latest commit 615ae2c
🔍 Latest deploy log https://app.netlify.com/sites/v3-meteor-api-docs/deploys/65fafb134672f1000889514d
😎 Deploy Preview https://deploy-preview-13052--v3-meteor-api-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

# Conflicts:
#	packages/mongo/mongo_livedata_tests.js
@nachocodoner nachocodoner marked this pull request as ready for review March 21, 2024 13:08
@nachocodoner nachocodoner changed the title [3.0] Create local doc when using collection operations in the same way Meteor 2.x [3.0] Report and extend test cases for the new async behaviors Mar 21, 2024
@nachocodoner nachocodoner merged commit 3e439df into release-3.0 Mar 21, 2024
@nachocodoner nachocodoner deleted the 350-persist-local-doc-temporary branch March 21, 2024 14:20
@filipenevola
Copy link
Contributor

Great explanation @nachocodoner.

I think the content of this entire PR discussion should be added to this page in the docs.

Newcomers can't learn this from a PR, and I don't believe it's useful only for experienced Meteor users. Even if it was, it should still be documented in the official docs 🥁.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants