即使禁用脱机,AWS AppSync查询也会返回缓存的响应



我有一个使用AWS AppSync的相当简单的节点应用程序。我能够成功地运行查询和突变,但我最近发现,如果我运行两次查询,我会得到相同的响应——即使我知道后端数据已经更改。在这种特殊的情况下,查询由lambda支持,在深入研究时,我发现查询似乎不会在网络上发送,因为lambda并不是每次运行时都会触发的,只是第一次。如果我使用控制台来模拟我的查询,那么一切都会正常运行。如果我重新启动我的应用程序,那么第一次运行查询时它可以正常工作,但连续的查询每次只返回相同的值。

以下是我代码的一部分:

client.query({
query: gql`
query GetAbc($cId: String!) {
getAbc(cId: $cId) {
id
name
cs
}
}`,
options: {
fetchPolicy: 'no-cache'
},
variables: {
cid: event.cid
}
})
.then((data) => {
// same data every time
})

编辑:尝试其他获取策略(如network-only)没有明显区别。

以下是我如何设置客户端的,不是超级干净,但它似乎可以工作:

const makeAWSAppSyncClient = (credentials) => {
return Promise.resolve(
new AWSAppSyncClient({
url: 'lalala',
region: 'us-west-2',
auth: {
type: 'AWS_IAM',
credentials: () => {
return credentials
}
},
disableOffline: true
})
)
}
getRemoteCredentials()
.then((credentials) => {
return makeAWSAppSyncClient(credentials)
})
.then((client) => {
return client.hydrated()
})
.then((client) => {
// client is good to use
})

getRemoteCredentials是一种将IoT身份验证转换为可与其他AWS SDK一起使用的普通IAM凭据的方法。这是有效的(因为如果不这样做,我就不会走得那么远)。

我的问题似乎与这一问题非常相似,GraphQL Query使用Apollo和AWS AppSync成功地运行了一次,但未能再次运行;我在节点环境中运行(而不是反应),但本质上是相同的问题。


我不认为这是相关的,但为了完整性,我应该提到,我已经尝试过使用和不使用文档中的设置代码。这似乎没有什么区别(除了烦人的日志记录,见下文),但它是:

global.WebSocket = require('ws')
global.window = global.window || {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
WebSocket: global.WebSocket,
ArrayBuffer: global.ArrayBuffer,
addEventListener: function () { },
navigator: { onLine: true }
}
global.localStorage = {
store: {},
getItem: function (key) {
return this.store[key]
},
setItem: function (key, value) {
this.store[key] = value
},
removeItem: function (key) {
delete this.store[key]
}
};
require('es6-promise').polyfill()
require('isomorphic-fetch')

这是从:https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-javascript.html

有了这段代码,在客户端设置中没有offlineDisabled: true,我看到控制台上不断喷出这行代码:

redux persistent-asyncLocalStorage需要全局localStorage对象。使用不同的存储后端,或者如果这是通用冗余您可能应该有条件地将应用程序持久化,如下所示:https://gist.github.com/rt2zz/ac9eb396793f95ff3c3b

然而,这与此问题没有明显区别。


更新:我的依赖项来自package.json,我在测试期间升级了这些依赖项,所以我的yarn.lock包含比这里列出的更新的修订。尽管如此:https://gist.github.com/macbutch/a319a2a7059adc3f68b9f9627598a8ca

更新#2:我还从CloudWatch日志中确认,查询只运行一次;我有一个定期在计时器上运行的突变,它被成功调用并在CloudWatch中可见。正如我所期望的那样,这是有效的,但查询不是。

更新#3:我已经调试到AppSync/Apolo代码,可以在apollo-client/core/QueryManager.js中看到我的fetchPolicy在该代码中被更改为"缓存优先"(我的评论):

QueryManager.prototype.fetchQuery = function (queryId, options, fetchType, fetchMoreForQueryId) {
var _this = this;
// Next line changes options.fetchPolicy to 'cache-first'
var _a = options.variables, variables = _a === void 0 ? {} : _a, _b = options.metadata, metadata = _b === void 0 ? null : _b, _c = options.fetchPolicy, fetchPolicy = _c === void 0 ? 'cache-first' : _c;
var cache = this.dataStore.getCache();
var query = cache.transformDocument(options.query);
var storeResult;
var needToFetch = fetchPolicy === 'network-only' || fetchPolicy === 'no-cache';
// needToFetch is false (because fetchPolicy is 'cache-first')
if (fetchType !== FetchType.refetch &&
fetchPolicy !== 'network-only' &&
fetchPolicy !== 'no-cache') {
// so we come through this branch
var _d = this.dataStore.getCache().diff({
query: query,
variables: variables,
returnPartialData: true,
optimistic: false,
}), complete = _d.complete, result = _d.result;
// here complete is true, result is from the cache
needToFetch = !complete || fetchPolicy === 'cache-and-network';
// needToFetch is still false
storeResult = result;
}
// skipping some stuff
...
if (shouldFetch) { // shouldFetch is still false so this doesn't execute
var networkResult = this.fetchRequest({
requestId: requestId,
queryId: queryId,
document: query,
options: options,
fetchMoreForQueryId: fetchMoreForQueryId,
}
// resolve with data from cache
return Promise.resolve({ data: storeResult });

如果我使用调试器将shouldFetch的值更改为true,那么至少我会看到一个网络请求发出,并执行我的lambda。我想我需要解开改变fetchPolicy的那一行正在做什么。

好的,我发现了问题。以下是我的问题中代码的缩写版本:

client.query({
query: gql`...`,
options: {
fetchPolicy: 'no-cache'
},
variables: { ... }
})

在这里更容易看出问题所在。这就是它应该是什么:

client.query({
query: gql`...`,
fetchPolicy: 'network-only'
variables: { ... }
})

我的原稿中有两个问题:

  1. fetchPolicy: 'no-cache'在这里似乎不起作用(我得到了一个空响应)
  2. fetchPolicy放在options对象中是不必要的

graphql客户端指定的选项不同,我们在两者之间切换。

在AWS Lambda函数中运行时,将查询fetch-policy设置为'network-only'

我建议使用WebSocketwindowlocalStorage的重写,因为这些对象并不真正应用于Lambda函数中。我通常在Lambda中用于NodeJS应用程序的设置如下所示。

'use strict';
// CONFIG
const AppSync = {
"graphqlEndpoint": "...",
"region": "...",
"authenticationType": "...",
// auth-specific keys
};
// POLYFILLS
global.WebSocket = require('ws');
global.window = global.window || {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
WebSocket: global.WebSocket,
ArrayBuffer: global.ArrayBuffer,
addEventListener: function () { },
navigator: { onLine: true }
};
global.localStorage = {
store: {},
getItem: function (key) {
return this.store[key]
},
setItem: function (key, value) {
this.store[key] = value
},
removeItem: function (key) {
delete this.store[key]
}
};
require('es6-promise').polyfill();
require('isomorphic-fetch');
// Require AppSync module
const AUTH_TYPE = require('aws-appsync/lib/link/auth-link').AUTH_TYPE;
const AWSAppSyncClient = require('aws-appsync').default;
// INIT
// Set up AppSync client
const client = new AWSAppSyncClient({
url: AppSync.graphqlEndpoint,
region: AppSync.region,
auth: {
type: AppSync.authenticationType,
apiKey: AppSync.apiKey
}
});

对于每个查询或/和初始化客户端,有两个选项可以使用AppSyncClient/ApolloClient启用/禁用缓存。

客户端配置:

client = new AWSAppSyncClient(
{
url: 'https://myurl/graphql',
region: 'my-aws-region',
auth: {
type: AUTH_TYPE.AWS_MY_AUTH_TYPE,
credentials: await getMyAWSCredentialsOrToken()
},
disableOffline: true
},
{
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'no-cache', // <-- HERE: check the apollo fetch policy options
errorPolicy: 'ignore'
},
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all'
}
}
}
);

备选方案:查询选项:

export default graphql(gql`query { ... }`, {
options: { fetchPolicy: 'cache-and-network' },
})(MyComponent);

有效的fetchPolicy值为:

  • 缓存优先:这是默认值,我们总是先尝试从缓存中读取数据。如果完成查询所需的所有数据都在缓存中,那么将返回该数据。Apollo只会在缓存结果不可用的情况下从网络中获取。此获取策略旨在最大限度地减少在呈现组件时发送的网络请求数
  • 缓存和网络:这个获取策略将让Apollo首先尝试从缓存中读取数据。如果完成查询所需的所有数据都在缓存中,那么将返回该数据。但是,无论完整数据是否在缓存中,此fetchPolicy都将始终使用网络接口执行查询,而cache-first仅在查询数据不在缓存中时才执行查询。此获取策略优化了用户获得快速响应的能力,同时还试图以额外的网络请求为代价保持缓存数据与服务器数据的一致性
  • 仅限网络:此获取策略永远不会从缓存中返回初始数据。相反,它总是使用您的网络接口向服务器发出请求。这种获取策略优化了与服务器的数据一致性,但代价是在用户可用时立即响应
  • 仅缓存:此获取策略永远不会使用您的网络接口执行查询。相反,它将始终尝试从缓存中读取。如果缓存中不存在查询的数据,则会引发错误。此获取策略允许您只与本地客户端缓存中的数据进行交互,而不进行任何网络请求,这使您的组件保持快速,但意味着您的本地数据可能与服务器上的数据不一致。如果您只想与ApolloClient缓存中的数据交互,请务必查看ApolloCustomer实例上可用的readQuery()readFragment()方法
  • 无缓存:此获取策略永远不会从缓存中返回初始数据。相反,它总是使用您的网络接口向服务器发出请求。与仅网络策略不同,它也不会在查询完成后将任何数据写入缓存

复制自:https://www.apollographql.com/docs/react/api/react-hoc/#graphql-查询选项

最新更新