springboot + neo4j + gremlin 排錯(cuò)記錄

起因:
  1. 我們?cè)?gremlin 的配置類(lèi)中向springboot 分別注入 Neo4JGraph 和 GraphTraversalSource 兩個(gè)bean
@Configuration
public class GraphDatabaseInitConfig {

    @Value("${spring.data.neo4j.password}")
    private String neo4jPassword;
    @Value("${spring.data.neo4j.username}")
    private String neo4jUsername;
    @Value("${spring.data.neo4j.uri}")
    private String neo4jUri;

    @Bean
    public Neo4JGraph createNeo4jGraph() {
        Driver driver = GraphDatabase.driver(neo4jUri, AuthTokens.basic(neo4jUsername, neo4jPassword));
        Neo4JNativeElementIdProvider vertexIdProvider = new Neo4JNativeElementIdProvider();
        Neo4JNativeElementIdProvider edgeIdProvider = new Neo4JNativeElementIdProvider();
        Neo4JGraph neo4JGraph = new Neo4JGraph(driver, null, vertexIdProvider, edgeIdProvider);

        return neo4JGraph;
    }

    @Bean
    public GraphTraversalSource createGraphTraversalSource() {
        return createNeo4jGraph().traversal();
    }

}
  1. 在不修改 neo4j 數(shù)據(jù)的情況下,查詢(xún)數(shù)據(jù)一直正常
  2. 在修改玩 neo4j 數(shù)據(jù)之后铐然,再次查詢(xún),剛開(kāi)始是正確的數(shù)據(jù)趋距,多查幾次后發(fā)現(xiàn)數(shù)據(jù)又變成了修改之前的數(shù)據(jù)
pom.xml 文件依賴(lài)
 <dependency>
            <groupId>com.steelbridgelabs.oss</groupId>
            <artifactId>neo4j-gremlin-bolt</artifactId>
            <version>0.4.6</version>
        </dependency>
排查過(guò)程
  1. 查看查詢(xún)的源碼,斷點(diǎn)跟進(jìn)搪桂,沒(méi)有發(fā)現(xiàn)問(wèn)題
  2. 查看 neo4j-gremlin-bolt jar包內(nèi)的類(lèi)寒砖,打斷點(diǎn)跟進(jìn)發(fā)現(xiàn)數(shù)據(jù)不變的情況下,每次查詢(xún) session(Neo4JGraph.currentSession()) 都會(huì)重新創(chuàng)建映九,但是當(dāng)數(shù)據(jù)發(fā)生變動(dòng)之后,session 會(huì)從之前所有查詢(xún)歷史中選擇某一個(gè) session 去進(jìn)行處理瞎颗。
  3. 上面的是假象件甥,其實(shí)數(shù)據(jù)不變的情況下,即使獲取到之前的session 哼拔,數(shù)據(jù)也完全一樣引有,查詢(xún)結(jié)果也一樣。
原因分析
  1. Neo4JGraph 的歷史 session 存儲(chǔ)在 ThreadLocal 的map中倦逐,但查詢(xún)完成后線(xiàn)程沒(méi)有殺掉譬正。
  2. springboot 的線(xiàn)程也就是一個(gè) Bean 持續(xù)存在,當(dāng)某次查詢(xún)跟之前查詢(xún)屬于同一個(gè)線(xiàn)程的情況下,Neo4JGraph.currentSession() 會(huì)從 ThreadLocal 的map中讀取 session 信息曾我, 也就是不會(huì)再創(chuàng)建新的 session粉怕。
解決方案
  1. 在每次調(diào)用查詢(xún)之后主動(dòng)關(guān)閉
neo4JGraph.close()
  1. aop 切片,在每次請(qǐng)求之后抒巢,執(zhí)行session 關(guān)閉
補(bǔ)充 Neo4JGraph 流程例如:
graphTraversalSource.V().has(LabelNameConstant.EQUIP, "name", equipTypeName).toList()

V() 和 has() 會(huì)記錄成 step
toList 之后才會(huì)真正觸發(fā)查詢(xún)

this.fill(new ArrayList<>())

Traversal.class

while (true) {
                final Traverser<E> traverser = endStep.next();
                TraversalHelper.addToCollection(collection, traverser.get(), traverser.bulk());
            }

GraphStep.class

this.iteratorSupplier = () -> (Iterator<E>) (Vertex.class.isAssignableFrom(this.returnClass) ?
                this.getTraversal().getGraph().get().vertices(this.ids) :
                this.getTraversal().getGraph().get().edges(this.ids));
this.getTraversal().getGraph().get().vertices(this.ids) 

Neo4JGraph.class

@Override
    public Iterator<Vertex> vertices(Object... ids) {
        // get current session
        Neo4JSession session = currentSession();
        // transaction should be ready for io operations
        transaction.readWrite();
        // find vertices, 此處第一次查詢(xún)時(shí)贫贝,會(huì)從數(shù)據(jù)庫(kù)進(jìn)行match
        return session.vertices(ids);
    }

Neo4JSession.class

public Iterator<Vertex> vertices(Object[] ids) {
        Objects.requireNonNull(ids, "ids cannot be null");
        // verify identifiers
        verifyIdentifiers(Vertex.class, ids);
        // check we have all vertices already loaded ,此處判斷是否是第一次查詢(xún)
        if (!verticesLoaded) {
            // check ids
            if (ids.length > 0) {
                // parameters as a stream
                Set<Object> identifiers = Arrays.stream(ids).map(id -> processIdentifier(vertexIdProvider, id)).collect(Collectors.toSet());
                // filter ids, remove ids already in memory (only ids that might exist on server)
                List<Object> filter = identifiers.stream().filter(id -> !vertices.containsKey(id) && !transientVertexIndex.containsKey(id)).collect(Collectors.toList());
                // check we need to execute statement in server
                if (!filter.isEmpty()) {
                    // vertex match predicate
                    String predicate = partition.vertexMatchPredicate("n");
                    // change operator on single id filtering (performance optimization)
                    if (filter.size() == 1) {
                        // execute statement
                        Result result = executeStatement("MATCH " + generateVertexMatchPattern("n") + " WHERE " + vertexIdProvider.matchPredicateOperand("n") + " = $id" + (predicate != null ? " AND " + predicate : "") + " RETURN n", Collections.singletonMap("id", filter.get(0)));
                        // create stream from query
                        Stream<Vertex> query = vertices(result);
                        // combine stream from memory and query result
                        Iterator<Vertex> iterator = combine(Stream.concat(identifiers.stream().filter(vertices::containsKey).map(id -> (Vertex)vertices.get(id)), identifiers.stream().filter(transientVertexIndex::containsKey).map(id -> (Vertex)transientVertexIndex.get(id))), query);
                        // process summary (query has been already consumed by combine)
                        ResultSummaryLogger.log(result.consume());
                        // return iterator
                        return iterator;
                    }
                    // execute statement
                    Result result = executeStatement("MATCH " + generateVertexMatchPattern("n") + " WHERE " + vertexIdProvider.matchPredicateOperand("n") + " IN $ids" + (predicate != null ? " AND " + predicate : "") + " RETURN n", Collections.singletonMap("ids", filter));
                    // create stream from query
                    Stream<Vertex> query = vertices(result);
                    // combine stream from memory and query result
                    Iterator<Vertex> iterator = combine(Stream.concat(identifiers.stream().filter(vertices::containsKey).map(id -> (Vertex)vertices.get(id)), identifiers.stream().filter(transientVertexIndex::containsKey).map(id -> (Vertex)transientVertexIndex.get(id))), query);
                    // process summary (query has been already consumed by combine)
                    ResultSummaryLogger.log(result.consume());
                    // return iterator
                    return iterator;
                }
                // no need to execute query, only items in memory
                return combine(identifiers.stream().filter(vertices::containsKey).map(id -> (Vertex)vertices.get(id)), identifiers.stream().filter(transientVertexIndex::containsKey).map(id -> (Vertex)transientVertexIndex.get(id)));
            }
            // vertex match predicate
            String predicate = partition.vertexMatchPredicate("n");
            // execute statement
            Result result = executeStatement("MATCH " + generateVertexMatchPattern("n") + (predicate != null ? " WHERE " + predicate : "") + " RETURN n", Collections.emptyMap());
            // create stream from query
            Stream<Vertex> query = vertices(result);
            // combine stream from memory (transient) and query result
            Iterator<Vertex> iterator = combine(transientVertices.stream().map(vertex -> (Vertex)vertex), query);
            // process summary (query has been already consumed by combine)
            ResultSummaryLogger.log(result.consume());
            // it is safe to update loaded flag at this time
            verticesLoaded = true;
            // return iterator
            return iterator;
        }
        // check ids
        if (ids.length > 0) {
            // parameters as a stream (set to remove duplicated ids)
            Set<Object> identifiers = Arrays.stream(ids).map(id -> processIdentifier(vertexIdProvider, id)).collect(Collectors.toSet());
            // no need to execute query, only items in memory
            return combine(identifiers.stream().filter(vertices::containsKey).map(id -> (Vertex)vertices.get(id)), identifiers.stream().filter(transientVertexIndex::containsKey).map(id -> (Vertex)transientVertexIndex.get(id)));
        }
        // no need to execute query, all items in memory, 后面的數(shù)據(jù)全部從內(nèi)存中備份出來(lái)
        return combine(transientVertices.stream().map(vertex -> (Vertex)vertex), vertices.values().stream().map(vertex -> (Vertex)vertex));
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛉谜,一起剝皮案震驚了整個(gè)濱河市稚晚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌型诚,老刑警劉巖客燕,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異俺驶,居然都是意外死亡幸逆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)暮现,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人楚昭,你說(shuō)我怎么就攤上這事栖袋。” “怎么了抚太?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵塘幅,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我尿贫,道長(zhǎng)电媳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任庆亡,我火速辦了婚禮匾乓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘又谋。我一直安慰自己拼缝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布彰亥。 她就那樣靜靜地躺著咧七,像睡著了一般。 火紅的嫁衣襯著肌膚如雪任斋。 梳的紋絲不亂的頭發(fā)上继阻,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼瘟檩。 笑死抹缕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芒帕。 我是一名探鬼主播歉嗓,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼背蟆!你這毒婦竟也來(lái)了鉴分?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤带膀,失蹤者是張志新(化名)和其女友劉穎志珍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體垛叨,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伦糯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嗽元。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敛纲。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖剂癌,靈堂內(nèi)的尸體忽然破棺而出淤翔,到底是詐尸還是另有隱情,我是刑警寧澤佩谷,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布旁壮,位于F島的核電站,受9級(jí)特大地震影響谐檀,放射性物質(zhì)發(fā)生泄漏抡谐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一桐猬、第九天 我趴在偏房一處隱蔽的房頂上張望麦撵。 院中可真熱鬧,春花似錦课幕、人聲如沸厦坛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杜秸。三九已至,卻和暖如春润绎,著一層夾襖步出監(jiān)牢的瞬間撬碟,已是汗流浹背诞挨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呢蛤,地道東北人惶傻。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像其障,于是被迫代替她去往敵國(guó)和親银室。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1. 什么是Neo4j? Neo4j是一個(gè)高性能的NOSQL圖形數(shù)據(jù)庫(kù)励翼,它將結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)在網(wǎng)絡(luò)上而不是表中蜈敢。它是...
    Whoami令狐沖閱讀 532評(píng)論 0 3
  • 根據(jù)(thirsd的個(gè)人空間 - 嗶哩嗶哩 ( ゜- ゜)つロ 乾杯~ Bilibili[https://spac...
    thirsd閱讀 1,383評(píng)論 0 0
  • 1.Neo4j簡(jiǎn)介 Neo4j是一個(gè)高性能的,NOSQL圖形數(shù)據(jù)庫(kù),它將結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)在網(wǎng)絡(luò)上而不是表中汽抚。它是一個(gè)...
    dalaoyang閱讀 2,091評(píng)論 0 4
  • Springboot整合neo4j OGM框架實(shí)踐 1. 數(shù)據(jù)準(zhǔn)備 首先,準(zhǔn)備往你的測(cè)試使用的neo4j數(shù)據(jù)庫(kù)中倒...
    ZackJiang閱讀 7,870評(píng)論 0 2
  • 環(huán)境 Linux環(huán)境主要是Centos和Ubuntu 由于tinkerpop是運(yùn)行在jvm上的抓狭,因此需要預(yù)先安裝j...
    iamrenpeng閱讀 6,389評(píng)論 3 5