useStreamFetchByKeys is missing closeHandler argument

The documentation for DAML react says that useStreamFetchByKeys can take an extra parameter:

"@param closeHandler A callback that will be called if the underlying
 *        WebSocket connection fails in an unrecoverable way."

However the code does not reflect this, no such parameter can be passed in. Sometimes the websocket connection fails and I need a way to handle this.

Note this is with version 1.11.1

3 Likes

Thanks for reporting this, seems like the whole docstring for that function contains a few issues, probably due to some copy-paste.

1 Like

I created a PR to fix this here.

1 Like

And @Gary_Verhaegen actually fixed the issue by adding the closeHandler back here.

1 Like

Hi @huw,

This is an oversight on my end. Sorry about it!

The functionality is there, but not exposed by the default ledger methods. This will be fixed (probably in 1.14, as the 1.13 release process has already started),

I unfortunately don’t have a good workaround to recommend in the meantime.

1 Like

Thanks guys. For now I plan to use useQuery instead of useStreamFetchByKeys.

1 Like

Sorry I should mention this. When I use useStreamFetchByKeys I was consistently getting websocket connection failures. That is why I was looking for a handler for this error. However, when I use uesQuery I have never noticed any websocket failures. Note: I’m using the sandbox - spinning up everything with the daml start command. Don’t know if this is a separate issue.

1 Like

Hi @huw,

This does sound like a separate issue. As far as I understand they both go through the same code paths so I would not expect any difference in behaviour there, but then again most bugs are things developers would not expect. Can you give us any more information on how to reproduce this issue? Would it be possible for you to produce and share a minimal project that shows the problem?

1 Like

Hi @Gary_Verhaegen , when useStreamFetchByKeys should return an empty result (no keys match the provided keys), then it returns (as expected) no contracts. If there are contracts matching then it results in websocket failures immediately.

1 Like

Hi @huw,

I am going to need a little bit more help in order to reproduce the issue, as I cannot seem to get it to fail. Here is what I’ve done.

  1. Run daml new --template=create-daml-app t, then cd t
  2. Open up daml/User.daml and add:
template Counter with
    owner: Party
    count: Int
  where
    signatory owner
    key owner : Party
    maintainer key
    preconsuming choice Increment: ContractId Counter with
      controller owner
      do
        create this with count = count + 1
  1. Create a new file ui/src/component/Counter.tsx with:
import React from 'react'
import { List, ListItem } from 'semantic-ui-react';
import { User } from '@daml.js/t';
import { useStreamFetchByKeys, useParty } from '@daml/react';

const Counter: React.FC = () => {
  const me = useParty();
  const result = useStreamFetchByKeys(User.Counter, () => [me], []);

  return (
    <List relaxed>
      {result.contracts.map(message => {
        return (
          <ListItem
            className='test-select-message-item'
            key={1}>
            <strong>{message ? message.payload.count : "not found"}</strong>
          </ListItem>
        );
      })}
    </List>
  );
};

export default Counter;
  1. Open ui/src/components/MainView.tsx and add import Counter from './Counter'; at the top, then
            <Segment>
              <Counter />
            </Segment>

just before </Grid.Column> near the end of the file.

  1. Run daml start, and then in another terminal, in the ui folder, npm install followed by npm start.

Log in as alice and you should see a websocket connection for the counter. You can create the counter with:

curl 'http://localhost:3000/v1/create' \
  -H 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJ0LXNhbmRib3giLCJhcHBsaWNhdGlvbklkIjoidCIsImFjdEFzIjpbImFsaWNlIl19fQ.y7x_iLwmFSn9m3RdWSBQWndjxI0zttWhc8ESNWRNymM' \
  -H 'content-type: application/json' \
  --data-raw '{"templateId":"User:Counter","payload":{"owner":"alice","count":0}}'

and increment it with:

curl 'http://localhost:3000/v1/exercise' \
  -H 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJ0LXNhbmRib3giLCJhcHBsaWNhdGlvbklkIjoidCIsImFjdEFzIjpbImFsaWNlIl19fQ.y7x_iLwmFSn9m3RdWSBQWndjxI0zttWhc8ESNWRNymM' \
  -H 'content-type: application/json' \
  --data-raw '{"templateId":"User:Counter","key":"alice","choice":"Increment","argument":{}}' 

and, at least for me, this all works as expected and I can see the counter incrementing “in real time”.

Could you please:

  • Try to follow the above steps and report whether that works as expected for you?
  • If it does, try to come up with a similarly detailed account of how to reproduce the failure you see in your application?
1 Like

@Gary_Verhaegen apologies I haven’t had a chance to get back to you. I’ll take a look into your example and hopefully I can tweak it in order to reproduce the problem I was experiencing.

1 Like

@Gary_Verhaegen tried your example. But I get an error in compilation when doing npm start:

./src/components/LoginScreen.tsx
Module not found: Can't resolve '@daml.js/t' in 'P:\code\t\ui\src\components'
1 Like

Hi @huw,

I have just ran through my instructions again and it all works as expected on my machine. I believe your issue may be caused by a timing issue between npm install and daml start. You have to make sure you do not run npm install before daml start has finished generating all of the JS code. I’d recommend waiting until you see the

   ____             ____
  / __/__ ____  ___/ / /  ___ __ __
 _\ \/ _ `/ _ \/ _  / _ \/ _ \\ \ /
/___/\_,_/_//_/\_,_/_.__/\___/_\_\

banner to be sure.

Please report on whether that helps.

1 Like

I had some time to debug this further and it was a file system permissions issue on my end causing the problem with not being able to resolve the “Module not found” error. I was using a network drive, so stopped using that and it fixed it. In order to get it to compile and then replicate the issue I had to do the following:

Change daml/User.daml file so it has an additional component to the key:

template Counter with
    owner: Party
    count: Int
    code : Int
  where
    signatory owner
    key (owner, code) : (Party, Int)
    maintainer key._1
    preconsuming choice Increment: ContractId Counter with
      controller owner
      do
        create this with count = count + 1

From t folder, run daml start.

Change the components/Counter.tsx to add a button to change the key:

import React, { useState } from 'react'
import { Button, List, ListItem } from 'semantic-ui-react';
import { User } from '@daml.js/t-0.1.0/lib';
import { useStreamFetchByKeys, useParty } from '@daml/react';

const Counter: React.FC = () => {
  const me = useParty();
  const [code, setCode] = useState("0");
  const result = useStreamFetchByKeys(
    User.Counter,
    () => [{_1: me, _2: code}], [code]
  );

  return (
    <div>
      <Button onClick={_ => setCode((parseInt(code) + 1).toString())}>Increment code</Button>
      <List relaxed>
        {result.contracts.map(message => {
          return (
            <ListItem
              className='test-select-message-item'
              key={1}>
              <strong>{message ? message.payload.count : "not found"}</strong>
            </ListItem>
          );
        })}
      </List>
    </div>
  );
};

export default Counter;

In all the typescript code change the imports from '@daml.js/t' to '@daml.js/t-0.1.0/lib' Then from ui folder:

npm install daml.js/t-0.1.0
npm install
npm start

(I know I shouldn’t have to change all the imports or do npm install daml.js/t-0.1.0 but I can’t seem to get it compiling otherwise).

Login as alice and open up the JS console. Then rapidly double click the “Increment code” button, I see “useStreamFetchByKeys: WebSocket connection failed.” error in the console. The app still seems to keeping working after this if you click the button normally again. In my original code my app didn’t seem to recover, unfortunately can’t share the original code, so not sure if this is the same problem.

1 Like

Hi @huw,

I must say I’m very puzzled by all this. If I follow your instructions exactly, I get a working system. No error. If I then rename all the @daml.js/t-0.1.0/lib to just @daml.js/t (then clear rm -rf node_modules package-lock.json and manually remove the dependency on daml.js/t-0.1.0 from package.lock that the npm install command had added) then rebuild (npm install && npm start), I still get a fully functioning system. I seem completely unable to reproduce any of the issues you describe.

So let’s dig a little bit deeper, I guess. Can you share the exact version of Daml you’re using, as well as the operating system (and version) you’re working on?

Regarding the fact that your app now works despite the WS errors, this is likely because, with your changes to Counter, every time you click on the button you open a new WS channel to stream a different key (as the code is part fo the key and gets incremented by the button), so I guess it doesn’t matter all that much that the old WS died. In my version of Counter, we rely on a single channel to get all the updates, so the UI will only increment the counter if the WS channel actually works.

1 Like

Yes it is very odd. I’m using DAML 1.11.1 and OS is Windows Server 2016. When you have an NPM dependency which is from a local directory it tries to create a symlink inside your node_modules pointing to the local directory where the dependency is. This is where I suspect there might be a problem - such as when I tried on a network drive I didn’t have permissions to create symlinks. The symlink problem doesn’t seem to get displayed in the NPM output, so the error occurs silently.
.

1 Like

Hi @huw,

I’ve tried to reproduce your issue on a Windows 2016 machine, but I failed. I double-checked that I did not have permission to create symlinks (mklink does give me a permission error), but npm install still worked as expected without the manual npm install daml.js/t-0.1.0 step. On the WebSocket side, the only error message I managed to reproduce, on either Windows or macOS, is the following:

The connection to ws://localhost:3000/v1/stream/fetch was interrupted while the page was loading.

This error occurs only on Firefox (not on Safari nor Chrome; I have not checked IE/Edge), when following your modified Counter example, about 20 seconds after pressing the Increment code button. It looks like something bout the way in which we close the underlying WebSocket connection when unmounting the (previous “code” value) component is not quite as Firefox wants it, but I believe the message to be ultimately benign as the only consequence seems to be that Firefox “forcibly” closes a WS connection which was, as far as either the server or the local JS code are concerned, already closed.

If you’re still experiencing any other error than that one, I’m at a loss as to how to reproduce it. I’ll happily try following other steps if you have further ideas, but otherwise I’m afraid I won’t be able to help.

2 Likes

Thanks for your assistance @Gary_Verhaegen . Yes the error appears benign for this example, although in my original code (which I can’t show) the error seemed to cause the UI to stop working. If I encounter this (fatal) error again I’ll let you know.

1 Like