即使禁用离线,AWS AppSync 查询 returns 缓存响应

AWS AppSync query returns cached response even when offline is disabled

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

这是我的部分代码:

  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 Runs Sucessfully One Time and Fails To Run Again using Apollo and AWS AppSync;我在节点环境中 运行ning(而不是反应),但它本质上是同一个问题。


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

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-persist asyncLocalStorage requires a global localStorage object. Either use a different storage backend or if this is a universal redux application you probably should conditionally persist like so: https://gist.github.com/rt2zz/ac9eb396793f95ff3c3b

然而,这对这个问题没有明显的影响。


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

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

更新 #3:我已经调试到 AppSync/Apollo 代码,可以看到我的 fetchPolicy 正在更改为 'cache-first' apollo-client/core/QueryManager.js(评论我的):

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 的那一行正在做什么。

当在 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
    }
});

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

 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 客户端指定的选项不同,我们在两者之间切换。

对于 enable/disable 在初始化客户端时的每个查询 or/and,使用 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 值为:

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

复制自:https://www.apollographql.com/docs/react/api/react-hoc/#graphql-options-for-queries