{"id":2027,"date":"2025-11-28T15:58:06","date_gmt":"2025-11-28T14:58:06","guid":{"rendered":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/?p=2027"},"modified":"2025-12-12T10:26:54","modified_gmt":"2025-12-12T09:26:54","slug":"implementing-a-lock-in-elasticsearch","status":"publish","type":"post","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/implementer-un-lock-en-elasticsearch\/","title":{"rendered":"Impl\u00e9menter un lock en Elasticsearch"},"content":{"rendered":"<p>A <a href=\"https:\/\/kestra.io\">Kestra<\/a>, la plateforme d&rsquo;orchestration et de scheduling de workflow pour laquelle je travaille, nous avons deux impl\u00e9mentations de repository: JDBC (avec support pour H2, MySQL et Postgres) et Elasticsearch.<\/p>\n<p>En JDBC, locker un enregistrement pour le traiter est assez facile :<\/p>\n<ol>\n<li><code>SELECT FOR UPDATE ... WHERE ...<\/code> va poser un lock sur le ou les enregistrements<\/li>\n<li>On traite le ou les enregistrements dans la m\u00eame transaction<\/li>\n<li><code>UPDATE...<\/code><\/li>\n<\/ol>\n<p>En fin de transaction, la BDD va automatiquement rel\u00e2cher les locks obtenu via le <code>SELECT FOR UPDATE<\/code>.<\/p>\n<p>Mais Elasticsearch ne supporte ni les locks ni les transactions, existe-t-il un moyen de simuler un lock?<\/p>\n<h2>Index vs Doc API<\/h2>\n<p>Elasticsearch est en m\u00eame temps une base de donn\u00e9es NoSQL et un moteur de recherche.\nComme tout moteur de recherche, il se base sur une phase d&rsquo;indexation asynchrone.<\/p>\n<p>Cela a deux cons\u00e9quences :<\/p>\n<ol>\n<li>Une recherche peut ne pas retourner un document existant<\/li>\n<li>Une indexation va par d\u00e9faut \u00e9craser tout document existant<\/li>\n<\/ol>\n<p>Pour palier \u00e0 \u00e7a, il y a deux solutions :<\/p>\n<ol>\n<li>Utiliser la Doc API pour charger un document depuis son identifiant au lieu de faire une recherche. Pour que cela marche, il faut fixer l&rsquo;identifiant du document lors de son indexation et ne pas laisser Elasticsearch d\u00e9finir un identifiant \u00e0 notre place.<\/li>\n<li>Lors de la cr\u00e9ation d&rsquo;un lock, utiliser <code>opType: Create<\/code> pour \u00eatre s\u00fbr qu&rsquo;aucun document n&rsquo;existe pour un m\u00eame identifiant. Cela permet d&rsquo;\u00e9viter qu&rsquo;un enregistrement n&rsquo;ait \u00e9t\u00e9 cr\u00e9\u00e9 entre le moment o\u00f9 on regarde si un lock existe et celui o\u00f9 on cr\u00e9e le document de lock.<\/li>\n<\/ol>\n<h2>LockRepository<\/h2>\n<p>Voici une potentielle impl\u00e9mentation pour un repository CRUD de Lock:<\/p>\n<pre>\npublic Optional findById(String category, String id)  throws IOException {\n        GetRequest getRequest = new GetRequest.Builder()\n                .index(\"locks\")\n                .id(id)\n                .build();\n\n        GetResponse getResponse = client.get(getRequest, Lock.class);\n        return Optional.ofNullable(getResponse.source());\n}\n\npublic boolean create(Lock newLock) throws IOException {\n        try {\n                \/\/ we use OptType.Create, so if a doc already exists, it will refuse to index a new one.\n                IndexRequest request = new IndexRequest.Builder()\n                        .index(\"locks\")\n                        .id(newLock.uid())\n                        .document(newLock)\n                        .opType(OpType.Create)\n                        .build();\n                client.index(request);\n                return true;\n        } catch (IOException e) {\n                \/\/ for conflicts, we return false to indicate that we were not able to create the lock\n                if (e instanceof ResponseException respException &amp;&amp; \n                                respException.getResponse().getStatusLine().getStatusCode() == 409) {\n                        return false;\n                }\n                throw e;\n        }\n}\n\npublic void deleteById(String category, String id) throws IOException {\n        DeleteRequest request = new DeleteRequest.Builder()\n                .index(\"locks\")\n                .id(IdUtils.fromParts(category, id))\n                .build();\n\n        client.delete(request);\n}\n<\/pre>\n<h2>LockService<\/h2>\n<p>Nous allons maintenant impl\u00e9menter un <code>LockService<\/code> qui a une m\u00e9thode qui permet d&rsquo;ex\u00e9cuter un <code>Runnable<\/code> gard\u00e9 par un lock pour simuler une transaction.<\/p>\n<p>Le principe est le suivant :<\/p>\n<ul>\n<li>On lock :\n<ul>\n<li>Si un lock existe d\u00e9j\u00e0, on attend<\/li>\n<li>Sinon, on cr\u00e9e un lock, si la cr\u00e9ation retourne false \u00e7a indique un conflit, donc on attend<\/li>\n<li>Sinon, on a r\u00e9ussit \u00e0 poser un lock !<\/li>\n<\/ul><\/li>\n<li>On execute le <code>Runnable<\/code><\/li>\n<li>On d\u00e9-lock: on supprime le lock<\/li>\n<\/ul>\n<p>Voici un exemple d&rsquo;impl\u00e9mentation, notre impl\u00e9mentation est un peu plus complexe, mais l&rsquo;id\u00e9e est l\u00e0 :<\/p>\n<pre>\npublic void doInLock(String category, String id, Duration timeout, Runnable runnable) {\n        lock(id); \/\/ our real implementation handle a timeout\n        try {\n                runnable.run();\n        } finally {\n                unlock(id);\n        }\n}\n\nprivate void lock(String id, Duration timeout) throws LockException {\n    long deadline = System.currentTimeMillis() + timeout.toMillis();\n        do {\n                Optional existing = lockRepository.findById(category, id);\n                if (existing.isEmpty()) {\n                        \/\/ we can try to lock!\n                        Lock newLock = new Lock(id, Instant.now());\n                        if (lockRepository.create(newLock)) {\n                        \/\/ yeah, we locked!\n                                return true;\n                        }\n\n                try {\n                        Thread.sleep(1); \/\/ this is not ideal, you can also use Thread.onSpinWait()\n                } catch (InterruptedException e) {\n                        Thread.currentThread().interrupt();\n                        throw new LockException(e);\n                }\n        } while (System.currentTimeMillis() &lt; deadline));\n}\n\nprivate void unlock(String category, String id) {\n        Optional existing = lockRepository.findById(category, id);\n        if (existing.isEmpty()) {\n                \/\/ should not occur but better be safe\n                return;\n        }\n\n        if (!existing.get().getOwner().equals(ServerInstance.INSTANCE_ID)) {\n        \/\/ if the lock has been acquire by someone else in-between we didn't release it\n        return;\n    }\n\n        lockRepository.deleteById(category, id);\n}\n<\/pre>\n<p>Cette impl\u00e9mentation n&rsquo;est pas une impl\u00e9mentation parfaite d&rsquo;un lock distribu\u00e9, comme expliqu\u00e9 plus  haut, l&rsquo;impl\u00e9mentation r\u00e9elle est plus complexe. Entre autre, Kestra a un m\u00e9canisme de liveness check distribu\u00e9 qui nous permet de supprimer les locks d&rsquo;une instance d\u00e9tect\u00e9e comme morte.<\/p>","protected":false},"excerpt":{"rendered":"<p>A Kestra, la plateforme d&rsquo;orchestration et de scheduling de workflow pour laquelle je travaille, nous avons deux impl\u00e9mentations de repository: JDBC (avec support pour H2, MySQL et Postgres) et Elasticsearch. En JDBC, locker un enregistrement pour le traiter est assez facile : SELECT FOR UPDATE &#8230; WHERE &#8230; va poser un lock sur le ou les enregistrements On traite le ou les enregistrements dans la m\u00eame transaction UPDATE&#8230; En fin de transaction, la BDD va automatiquement rel\u00e2cher les locks obtenu&#8230;<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/implementer-un-lock-en-elasticsearch\/\">Lire la suite<span class=\"screen-reader-text\"> Lire la suite<\/span><\/a><\/p><\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[9],"tags":[231],"class_list":["post-2027","post","type-post","status-publish","format-standard","hentry","category-informatique","tag-elasticsearch"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":1650,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/le-profiler-sql-de-visualvm\/","url_meta":{"origin":2027,"position":0},"title":"Le profiler SQL de VisualVM","author":"admin","date":"mardi  4 avril 2023","format":false,"excerpt":"Il y a peu, j'ai d\u00e9couvert le profiler SQL de VisualVM et je me suis dit que je devais aussi vous le faire d\u00e9couvrir ;). VisualVM est un outil qui fournit une interface visuelle pour afficher des informations d\u00e9taill\u00e9es sur les applications qui s'ex\u00e9cutent sur une machine virtuelle Java (JVM).\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/Capture-decran-du-2023-04-03-14-17-25-1024x624.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/Capture-decran-du-2023-04-03-14-17-25-1024x624.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/Capture-decran-du-2023-04-03-14-17-25-1024x624.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":1786,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/concevoir-un-saas-multitenant\/","url_meta":{"origin":2027,"position":1},"title":"Concevoir un SaaS multitenant","author":"admin","date":"mardi  5 mars 2024","format":false,"excerpt":"Cet article se repose sur mon talk Concevoir un SaaS multitenant fait \u00e0 Cloud Nord le 12 octobre 2023. Kestra est une plate-forme d\u2019orchestration et de scheduling de donn\u00e9e hautement scalabe, qui cr\u00e9e, ex\u00e9cute, planifie et surveille des millions de pipelines complexes. Pour une introduction \u00e0 Kestra, vous pouvez lire\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/kestra-software-architecture-1024x576.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/kestra-software-architecture-1024x576.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/kestra-software-architecture-1024x576.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":1731,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/optimisation-dindex-postgresql\/","url_meta":{"origin":2027,"position":2},"title":"Optimisation d&rsquo;index PostgreSQL","author":"admin","date":"mardi 22 ao\u00fbt 2023","format":false,"excerpt":"Il y a quelque temps, j\u2019avais travaill\u00e9 sur des optimisations de temps d'ex\u00e9cution de requ\u00eates pour PostgreSQL, j'en parle ici : LE PROFILER SQL DE VISUALVM. Kestra est une plate-forme d\u2019orchestration et de scheduling de donn\u00e9e hautement scalable, qui cr\u00e9e, ex\u00e9cute, planifie, et surveille des millions de pipelines complexes. C\u2019est\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1138,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/ma-deuxieme-annee-chez-zenika\/","url_meta":{"origin":2027,"position":3},"title":"Ma deuxi\u00e8me ann\u00e9e chez Zenika","author":"admin","date":"jeudi  3 septembre 2020","format":false,"excerpt":"Aujourd\u2019hui est un jour sp\u00e9cial, cela fait deux ans que je suis arriv\u00e9 chez Zenika, apr\u00e8s 9 ans en tant qu\u2019architecte logiciel dans la DSI d\u2019un grand groupe de retail. L'ann\u00e9e derni\u00e8re j'avais fait le bilan de ma premi\u00e8re ann\u00e9e pass\u00e9e : 1 an chez Zenika. Et un an apr\u00e8s,\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":923,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/devoxx-france-2019\/","url_meta":{"origin":2027,"position":4},"title":"Devoxx France 2019","author":"admin","date":"lundi 13 mai 2019","format":false,"excerpt":"Cette ann\u00e9e, j'ai eu la chance d'assister \u00e0 Devoxx France, j'ai m\u00eame eu la chance d'\u00eatre speaker et de donner deux talks (Mes premiers pas en deeplearning avec Keras et Arthas - Alibaba Java Diagnostic Tool ), mais \u00e7a je vous en parlerais plus tard ;) Voici un petit compte\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1112,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/java-15-quoi-de-neuf\/","url_meta":{"origin":2027,"position":5},"title":"Java 15 : quoi de neuf ?","author":"admin","date":"jeudi  2 juillet 2020","format":false,"excerpt":"Maintenant que Java 15 est features complete (Rampdown Phase One au jour d\u2019\u00e9criture de l\u2019article), c\u2019est le moment de faire le tour des fonctionnalit\u00e9s qu\u2019apporte cette nouvelle version, \u00e0 nous, les d\u00e9veloppeurs. Cet article fait partie d\u2019une suite d\u2019article sur les nouveaut\u00e9s des derni\u00e8res versions de Java, pour ceux qui\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/2027","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/comments?post=2027"}],"version-history":[{"count":21,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/2027\/revisions"}],"predecessor-version":[{"id":2077,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/2027\/revisions\/2077"}],"wp:attachment":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/media?parent=2027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/categories?post=2027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/tags?post=2027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}