Why do GraphQL Subscriptions use an AsyncIterator? - graphql

AsyncIterator requires pulling data using .next(). But with websockets I generally want to push data when events occur. Only thing I can think of is that by using pull-based they can rate-limit.
So what is calling .next()? Is it a timer, or does it listen to a publish message, queue that, then call .next() until it consumes all the queue?
Is this suitable for real-time data, like GPS positions on a map?
Looked here and still could not figure it out: https://github.com/facebook/graphql/blob/master/rfcs/Subscriptions.md
GraphQL Subscriptions repo from Apollo: https://github.com/apollographql/graphql-subscriptions

AsyncIterator iterate through an EventStream, then each event is resolved, sometimes with filter and/or with a payload manipulation.
Payload manipulation can call an another async database request, or resolve other GraphQL Types, which is time consuming.
So GraphQL use a pull-based system to rate-limit resolves eventStream. If you don't use withFilter neither resolves, you won't have delay on Event except with a lot of user.
GraphQL is suitable for low latency data.
Source: https://github.com/graphql/graphql-js/blob/master/src/subscription/subscribe.js#L44

Related

What are Solidity Events

I have been struggling for quite some time with finding an explanation to what events are in Solidity (or in the Blockchain context). As far as I understand, they are a way of storing (or logging) information on the particular contract that can then be updated throughout the life of that contract. But how is this different than a plain ol' variable? Why can't I just create a variable that is then simply updated with new information?
Events in Solidity can be used to log certain events in EVM logs. These are useful when clients are required to be notified of any change or event in the contract. Or maybe in the future you need to search for something that has happened so you go through all the logs. These logs are stored on the blockchain in transaction logs. Logs cannot be accessed from the contracts but are used as a mechanism to notify change of state or the occurrence of an event in the contract. They help us write asynchronous applications.
Events are stored in the logsBloom which is in the header of each block.
Events are piece of data executed on the blockchain and stored in the blockchain but not accessible by any smart contracts. it is kinda console.log in javascript or print in python.
Events are much more gas efficient than using a storage variable
Events are useful for testing the contract. If you interact with oracles, you sometimes want to see if the function call by oracle service is done or not. To see if the function call is done, you emit the result of the function or one of the properties of the result.
Events are useful if you want to maintain the history/log of every change that happens in a mapping.
Deposit contracts were created on the Ethereum 1.0 chain. This kind of smart contract is used for depositing ETH on the beacon chain. An event is emitted every time a deposit is made.
There are two events that must be present in an ERC-20-compliant token:
Transfer : This event must trigger when tokens are transferred, including any zero-value transfers. The event is defined as follows:
event Transfer(address indexed _from, address indexed _to, uint256 _value)
Approval : This event must trigger when a successful call is made to the approve function.
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
You can read this article for deep dive: transaction-receipt-trie-and-logs-simplified
From the docs:
Solidity events give an abstraction on top of the EVM’s logging functionality. Applications can subscribe and listen to these events through the RPC interface of an Ethereum client.
It's easier for an off-chain app to subscribe to new event logs than to a variable change. Especially when the variable is not public.
Same goes for querying historical event logs (easy through the JSON-RPC API and its wrappers such as Web3 or Ethers.js), vs. historical changes of the variable (complicated, would need to query a node for each block and look for the changes proactively).
Example: The ERC-20 token standard defines the Transfer() event. A token contract emits this event each time a transfer (of its tokens) occurs. This allows a blockchain explorer (or any other off-chain app) to react to this event - for example to update their own database of token holders. Without the event, they would have no way (or a very complicated way at least) to learn about the transfer.
Solidity events are pieces of data that are emitted and stored in the blockchain. When you emit an event it creates a log that front-end applications can use to trigger changes in the UI.
It's a cheap form of storage.
You can define an event like this:
event Message(address indexed sender, address indexed recipient, string message);
and emit an event like this:
emit Message(msg.sender, _recipient, "Hello World!");
Read this article for more information and this one to learn how to get events in JavaScript using Ethers.js

GraphQL Asynchronous query results

I'm trying to implement a batch query interface with GraphQL. I can get a request to work synchronously without issue, but I'm not sure how to approach making the result asynchronous. Basically, I want to be able to kick off the query and return a pointer of sorts to where the results will eventually be when the query is done. I'd like to do this because the queries can sometimes take quite a while.
In REST, this is trivial. You return a 202 and return a Location header pointing to where the client can go to fetch the result. GraphQL as a specification does not seem to have this notion; it appears to always want requests to be handled synchronously.
Is there any convention for doing things like this in GraphQL? I very much like the query specification but I'd prefer to not leave the client HTTP connection open for up to a few minutes while a large query is executed on the backend. If anything happens to kill that connection the entire query would need to be retried, even if the results themselves are durable.
What you're trying to do is not solved easily in a spec-compliant way. Apollo introduced the idea of a #defer directive that does pretty much what you're looking for but it's still an experimental feature. I believe Relay Modern is trying to do something similar.
The idea is effectively the same -- the client uses a directive to mark a field or fragment as deferrable. The server resolves the request but leaves the deferred field null. It then sends one or more patches to the client with the deferred data. The client is able to apply the initial request and the patches separately to its cache, triggering the appropriate UI changes each time as usual.
I was working on a similar issue recently. My use case was to submit a job to create a report and provide the result back to the user. Creating a report takes couple of minutes which makes it an asynchronous operation. I created a mutation which submitted the job to the backend processing system and returned a job ID. Then I periodically poll the jobs field using a query to find out about the state of the job and eventually the results. As the result is a file, I return a link to a different endpoint where it can be downloaded (similar approach Github uses).
Polling for actual results is working as expected but I guess this might be better solved by subscriptions.

Can I trigger a Lambda function or SNS event in response to an AppSync GraphQL Mutation?

AWS AppSync/GraphQL subscriptions coupled with AWS Amplify sound amazing, since out of the box you can subscribe all your clients to high-level domain events specified in your schema.
Though it seems like a natural extension that there should be a way to broadcast all those events to a service like lambda or SNS to react internally to those events. Maybe you want to log the events, re-index or aggregate updated data, or send an email or push notification to a user. Just like DynamoDB Streams allows you to trigger a lambda upon to updates on a table. Is anyone aware of a good way to achieve this?
There appears to be no AppSync trigger source for lambda, but it seems like there might be a couple of ways to do this otherwise:
Create a long-running process/service on ECS/Fargate that subscribes to every mutation it wants to broadcast to SNS/Lambda. Not ideal as you would have to manage and scale that process yourself.
Use DynamoDB Streams as a lambda trigger. However, a DynamoDB table change event is lower-level than a GraphQL mutation event, and this assumes your only data source is DynamoDB, whereas AppSync/GraphQL can plug into many other data sources.
Create lambda resolvers for every mutation that also broadcast the event to SNS/Lambda. For simply broadcasting SNS events, perhaps there's a clever way to use pipeline resolvers to attach a reusable resolver to every mutation...
I'm not sure how the Amplify framework powers their #searchable transformer, whether its #2 or #3, or something else altogether, but it seems like that's in the same ballpark (re-indexing the data in Elasticsearch in response to an update). I do remember hearing you can write your own transformers ... perhaps one could write their own transformer #broadcasted that also broadcasts all mutation events to SNS 🤔
I'm surprised I haven't seen much if any discussion surrounding this topic. If anyone has some good ideas or if I'm thinking about this the wrong way let me know.
It sounds like you are trying to trigger lambda functions based on incoming mutations.
Like you mentioned in option 3 - lambda resolvers can achieve this functionality but if what you would like to do is invoke a common lambda that publishes to SNS as a step in the execution - then you can utilize Pipeline Resolvers. Where the 'common' lambda function will be the data source for the step in your pipeline and then have the regular resolver you would use for Dynamo/ES/Aurora Serverless.
Docs:
https://docs.aws.amazon.com/appsync/latest/devguide/pipeline-resolvers.html

How to cancel http requests made by Apollo (angular) client?

I noticed that when I unsubscribe from query, http request is still executing and not being canceled. Also tried to use AbortController but without any luck. How does one cancel http requests made by Apollo client?
This is an old question, but since I just wanted to do the same and managed to do it with the latest Apollo Client (3.4.13) and Apollo-Angular (2.6.0), you need to make sure that you're using watchQuery() instead of query(), and then call unsubscribe() on the returned Apollo subscription from the previous request. The latter implies of course that you should store somewhere the subscription object that you want to abort.
This is an old question, but I spent two days on this bananas problem and I want to share for posterity.
We're using Angular and GraphQL (apollo-angular and codegen to make GraphQL services) and we opted for an event-driven architecture using NgRx to send events and then perform http calls. When sending multiple identical events (but with different property values) we noticed we got stale data in some cases, especially edge cases like when 20+ of these identical events were sent. Obviously not common, but not ideal, and a hint of perhaps bad scale since we were going to need many more events in future.
The way we resolved this issue was by using .watch() instead of .fetch() on the GraphQL generated services. Initially, since .fetch() returned the same Observable as .watch().valueChanges, we thought it was easier and simpler to just use .fetch(), but their behavior seems much different. We were never able to cancel http requests performed by .fetch(). But after changing to .watch().valueChanges, the Observable acted exactly as http request Observables would, complete with -- thankfully -- cancelation.
So in NgRx, we swapped our generic mergeMap operator for the switchMap operator. This will ensure previous effects listening on dispatched events will be canceled. We needed no extra overhead, no .next-ing to Subjects, no extra Subscriptions. Just change .fetch() into .watch().valueChanges and then switchMap to your heart's content. The takeUntil operator will now also cancel these requests, which is our performed method of unsubscribing from Observables.
Sidenote: I'm amazed that this information was this hard to come by, and honestly this question and one GitHub issue was all I could find to intimate this discrepancy. Even now I don't quite understand why anyone would want .fetch() if all it does is perform an http call that will always resolve and then return an Observable that does not behave the way you expect Observables to behave.

How can I prevent unnecessary data transfer in WebApi?

I've got a situation where I have a WebApi endpoint that my page polls for data every minute or so. Each poll returns about 10KB of data, and I've realized that in many cases the data doesn't change from the previous poll, but it still eats up the bandwidth sending back the results.
So I'm wondering if there's a standard way to have WebApi determine that the results haven't changed AND to signal the browser that this is the case.
Because the endpoints are stateless, how could an endpoint know what the previous state was?
And how should it signal the client that this is the case? In most situations, I return a strongly typed object (like List<T>), so I can't instead return some other UseCachedVersion kind of object. I could return null, but that isn't as descriptive as I would like.
Are there any standard practices for this situation?
You use caching in the API/controller layer like CacheOutput. http://www.hanselman.com/blog/NuGetPackageOfTheWeekASPNETWebAPICachingWithCacheCowAndCacheOutput.aspx
If you need real-time update to the poll, you can use SignalR and just update the client/subscribers if the server makes a broadcast.

Resources