neo4j / neo4j-python-driver

Neo4j Bolt driver for Python
https://neo4j.com/docs/api/python-driver/current/
Other
899 stars 186 forks source link

Exception: Should only attempt to acquire lease when leader. #459

Closed ianhhhhhhhhe closed 4 years ago

ianhhhhhhhhe commented 4 years ago

Neo4j Version: 4.1.0 Enterprise
Neo4j Mode: Cluster
Driver version: Python driver 4.0.2
Operating System: Docker

The network has three neo4j database. Cluster1 is the leader, Cluster0 and Cluster2 are the follower. And my code is like:

createDriver function:

class Graph(object):

    def __init__(self, config):
        uri = config["neo4jUri"]
        addresses = config["neo4jAddress"]
        user = config["neo4jUser"]
        password = config["neo4jPass"]
        self._driver = self.createDriver(uri=uri, user=user, password=password, addresses=addresses)

    def createDriver(self, uri, user, password, addresses):
        def resolver(address):
            for address in addresses:
                yield address, 7687
        return GraphDatabase.driver(uri, auth=(user, password), resolver=resolver)

    def run_query(self, func, arg):
        with self._driver.session() as session:
            response = None
            try:
                response = session.write_transaction(func, arg)
            except ConstraintError:
                print("ConstraintError")
                return response
            except Exception as e:
                print("Exception: {0}".format(e))
            finally:
                return response

And when I only use Cluster1, it works just fine.

But when I use all three addresses, it won't work, and tell:

Exception: Should only attempt to acquire lease when leader.
martin-neotech commented 4 years ago

@ianhhhhhhhhe there is no Python Driver 4.0.4 https://pypi.org/project/neo4j/#history

ianhhhhhhhhe commented 4 years ago

@martin-neotech sorry, my mistake, it should be 4.0.2

martin-neotech commented 4 years ago

@ianhhhhhhhhe I will try to investigate this.

ianhhhhhhhhe commented 4 years ago

@martin-neotech I just try something: I put the leader address in the first element of the addresses. No exception. But if the first address is not the leader, the exception will be reported. So I think, maybe, the driver always use the first address as the leader.

martin-neotech commented 4 years ago

@ianhhhhhhhhe ok, nice that you found a solution. I will take a note about this use case and see if its a documentation issue or a specification issue, how the driver should resolve addresses against a cluster.

technige commented 4 years ago

@ianhhhhhhhhe Can you confirm that you are using routing here, i.e. you have a neo4j://... URI?

ianhhhhhhhhe commented 4 years ago

@technige Yes, I'm pretty sure that I used neo4j://, not bolt://. I think the reason why this error is rasied may be that the driver didn't check the role of the address when it's going to make a write_transaction. The driver just got the addresses and used the first address as the leader.

The code is here: neo4j/io/init.py#L564, this is where the driver get the addresses. And the code shows that only the first address is used. Maybe that's the reason?

martin-neotech commented 4 years ago

@ianhhhhhhhhe

See resolver function, https://neo4j.com/docs/driver-manual/4.1/client-applications/#driver-resolver-function

Try something like this.

Cluster0: neo4j://a.example.com:8000 Cluster1: neo4j://b.example.com:8001 Cluster2: neo4j://c.example.com:8002

class Graph(object):

    @classmethod
    def createDriver(cls, uri, user, password):

        def resolver(address):
            host, port = address
            if "x.example.com" == host: 
                  yield "a.example.com", 8000
                  yield "b.example.com", 8001
                  yield "c.example.com", 8002
             else:
                  yield host, port

        return GraphDatabase.driver(uri, auth=(user, password), resolver=resolver)

    def run_query(self, func, arg):
        with self._driver.session() as session:
            response = None
            try:
                response = session.write_transaction(func, arg)
            except ConstraintError:
                print("ConstraintError")
                return response
            except Exception as e:
                print("Exception: {0}".format(e))
            finally:
                return response
driver = Graph.createDriver("neo4j://x.example.com", user="neo4j", password="password")
ianhhhhhhhhe commented 4 years ago

@mariascharin I tried and failed, and it says:

Cannot resolve address neo4j://MYNEO4JADDRESS:7687

the addresses in the resolver can not have neo4j://

martin-neotech commented 4 years ago

@mariascharin I tried and failed, and it says:

Cannot resolve address neo4j://MYNEO4JADDRESS:7687

the addresses in the resolver can not have neo4j://

Sorry my bad, I have edited it now

ianhhhhhhhhe commented 4 years ago

@martin-neotech problem solved. Seems that the missing host check is the reason why I cannot connect to the LEADER. Thank you very much.