{"id":1048,"date":"2020-11-09T16:25:19","date_gmt":"2020-11-09T15:25:19","guid":{"rendered":"http:\/\/sematext.solr.pl\/?p=1048"},"modified":"2020-11-15T21:40:13","modified_gmt":"2020-11-15T20:40:13","slug":"introducing-solr-circuit-breakers","status":"publish","type":"post","link":"https:\/\/solr.pl\/en\/2020\/11\/09\/introducing-solr-circuit-breakers\/","title":{"rendered":"Introducing Solr Circuit Breakers"},"content":{"rendered":"\n<p>With the Solr 8.7 release, we were given a very useful feature called circuit breakers. A circuit breaker design pattern allows stopping execution when certain criteria are met. For example, when the memory usage is higher than defined a query execution may be stopped, or when the CPU usage on a given node is too high. Let&#8217;s look at what Solr 8.7 brings us.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">New Functionality<\/h2>\n\n\n\n<p>The new functionality that was introduced to Solr aims to prevent the execution of a request that causes a node to go beyond defined thresholds. For example, when the memory utilization of the JVM reaches 75% you may want to stop all requests that are hitting Solr for the ones that are processed to finish.<\/p>\n\n\n\n<p>That&#8217;s why with the Solr 8.7 a circuit breaker code was added with two circuit breaker implementations:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>JVM memory-based circuit breaker<\/li><li>CPU utilization based circuit breaker<\/li><\/ul>\n\n\n\n<p>So when to use the circuit breaker functionality? When you want to trade-in request throughput for stability. If you want more stability from your Solr nodes you include circuit breakers, if you want to go full throttle &#8211; you don&#8217;t.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Circuit Breaker Configuration<\/h2>\n\n\n\n<p>The circuit breaker configuration should be included in the&nbsp;<strong>solrconfig.xml<\/strong>&nbsp;and should be included inside the&nbsp;<strong>circuitBreaker<\/strong>&nbsp;tag:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;circuitBreaker class=\"solr.CircuitBreakerManager\" enabled=\"true\">\n...\n&lt;\/circuitBreaker><\/code><\/pre>\n\n\n\n<p>The&nbsp;<strong>enabled<\/strong>&nbsp;attribute turns the circuit breaker functionality on and off globally. When set to&nbsp;<strong>true<\/strong>&nbsp;they are enabled, when set to&nbsp;<strong>false<\/strong>&nbsp;they are disabled.&nbsp;<\/p>\n\n\n\n<p>At the moment of writing this blog post we could use two circuit breakers:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>CPU based<\/li><li>JVM memory based<\/li><\/ul>\n\n\n\n<p>The CPU based one tracks the CPU utilization and checks the average CPU usage over the last minute. If that crossed the defined threshold the circuit breaker is tripped and the execution of the request will be prevented. To enable it we would include the following properties inside the&nbsp;<strong>circuitBreaker<\/strong>&nbsp;tag:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;str name=\"cpuEnabled\">true&lt;\/str>\n&lt;str name=\"cpuThreshold\">75&lt;\/str><\/code><\/pre>\n\n\n\n<p>The first property enables the CPU based circuit breaker and the second one specifies the threshold.<\/p>\n\n\n\n<p>The JVM memory-based circuit breaker tracks the memory usage of the JVM and rejects the execution of the request if the usage is above the percentage of the maximum heap size &#8211; the one defined with the&nbsp;<strong>Xmx<\/strong>. To enable it we would include the following properties inside the&nbsp;<strong>circuitBreaker<\/strong>&nbsp;tag:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;str name=\"memEnabled\">true&lt;\/str>\n&lt;str name=\"memThreshold\">80&lt;\/str><\/code><\/pre>\n\n\n\n<p>The above configuration means that the JVM memory circuit breaker is enabled and it will be triggered when the JVM heap usage is above 80%. So if our Solr heap would be set to a maximum size of 10G the circuit breaker would prevent the execution of requests if the usage is above 8G.<\/p>\n\n\n\n<p>One thing to keep in mind is that the&nbsp;<strong>memThreshold<\/strong>&nbsp;can take the values between&nbsp;<strong>50<\/strong>&nbsp;and&nbsp;<strong>95<\/strong>.&nbsp;<\/p>\n\n\n\n<p>Our final configuration would look as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;circuitBreaker class=\"solr.CircuitBreakerManager\" enabled=\"true\">\n  &lt;str name=\"memEnabled\">true&lt;\/str>\n  &lt;str name=\"memThreshold\">80&lt;\/str>\n  &lt;str name=\"cpuEnabled\">true&lt;\/str>\n  &lt;str name=\"cpuThreshold\">75&lt;\/str>\n&lt;\/circuitBreaker><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How it Works<\/h2>\n\n\n\n<p>Once you have the circuit breakers defined and working, if they are tripped you will see a response like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n  \"responseHeader\":{\n    \"status\":503,\n    \"QTime\":0,\n    \"params\":{\n      \"json\":\"{\\n\\t\\\"query\\\": \\\"*:*\\\",\\n\\t\\\"facet\\\": {\\n\\t  \\\"test\\\": {\\n\\t    \\\"terms\\\": {\\n\\t      \\\"field\\\": \\\"text\\\"\\n\\t    }\\n\\t  }\\n\\t}\\n}\"}},\n  \"status\":\"FAILURE\",\n  \"error\":{\n    \"metadata\":[\n      \"error-class\",\"org.apache.solr.common.SolrException\",\n      \"root-error-class\",\"org.apache.solr.common.SolrException\"],\n    \"msg\":\"Circuit Breakers tripped Memory Circuit Breaker triggered as JVM heap usage values are greater than allocated threshold.Seen JVM heap memory usage 503369312 and allocated threshold 429496729\\n\",\n    \"code\":503}}<\/code><\/pre>\n\n\n\n<p>The response code&nbsp;<strong>503<\/strong>&nbsp;and the message about the tripped circuit breaker. In the above case, it was a memory circuit breaker. We should back-off and wait for the next request. An exponential back-off is a good practice. Though the circuit breakers do not add any significant overhead too many checks can cause performance overhead.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Downsides<\/h2>\n\n\n\n<p>Having circuit breakers in Solr doesn&#8217;t mean that you can&#8217;t go into out of memory situations. And this is the first downside of the functionality. To simulate that I experimented.<\/p>\n\n\n\n<p>I included the full example on our <a href=\"https:\/\/github.com\/solrpl\/blog\/tree\/master\/posts\/circuit\" target=\"_blank\" rel=\"noreferrer noopener\">Github<\/a> account and you can repeat what I did.<\/p>\n\n\n\n<p>I started with a simple, empty Solr 8.7 node, embedded Zookeeper and I created a simple collection that had the following circuit breaker configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;circuitBreaker class=\"solr.CircuitBreakerManager\" enabled=\"true\">\n  &lt;str name=\"memEnabled\">true&lt;\/str>\n  &lt;str name=\"memThreshold\">80&lt;\/str>\n  &lt;str name=\"cpuEnabled\">true&lt;\/str>\n  &lt;str name=\"cpuThreshold\">75&lt;\/str>\n&lt;\/circuitBreaker><\/code><\/pre>\n\n\n\n<p>Once the&nbsp;<strong>circuit<\/strong>&nbsp;collection was created I indexed 1.000.000 documents using a simple Python script. They were randomized and I tried to create a very high cardinality field. The fields section of the&nbsp;<strong>schema.xml<\/strong>&nbsp;looked as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;field name=\"id\" type=\"string\" indexed=\"true\" stored=\"true\" required=\"true\" multiValued=\"false\" docValues=\"true\" \/>\n&lt;field name=\"text\" type=\"text_ws\" indexed=\"true\" stored=\"true\" multiValued=\"false\" \/><\/code><\/pre>\n\n\n\n<p>After that was done I run a query like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">curl -XGET 'localhost:8983\/solr\/circuit\/select?q=*:*&amp;rows=0'<\/code><\/pre>\n\n\n\n<p>It resulted in a proper response:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n  \"responseHeader\":{\n    \"zkConnected\":true,\n    \"status\":0,\n    \"QTime\":60,\n    \"params\":{\n      \"q\":\"*:*\",\n      \"rows\":\"0\"}},\n  \"response\":{\"numFound\":1000000,\"start\":0,\"numFoundExact\":true,\"docs\":[]\n  }}<\/code><\/pre>\n\n\n\n<p>The second query looked as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">curl 'http:\/\/localhost:8983\/solr\/circuit\/query' -d '{\n  \"query\": \"*:*\",\n  \"limit\": 0,\n  \"facet\": {\n    \"test\": {\n      \"terms\": {\n\t\"field\": \"text\"\n      }\n    }\n  }\n}'<\/code><\/pre>\n\n\n\n<p>And then Solr responded with the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n  \"responseHeader\":{\n    \"zkConnected\":true,\n    \"status\":500,\n    \"QTime\":6893,\n    \"params\":{\n      \"json\":\"{\\n  \\\"query\\\": \\\"*:*\\\",\\n  \\\"limit\\\": 0,\\n  \\\"facet\\\": {\\n    \\\"test\\\": {\\n      \\\"terms\\\": {\\n\\t\\\"field\\\": \\\"text\\\"\\n      }\\n    }\\n  }\\n}\"}},\n  \"response\":{\"numFound\":1000000,\"start\":0,\"numFoundExact\":true,\"docs\":[]\n  },\n  \"error\":{\n    \"metadata\":[\n      \"error-class\",\"org.apache.solr.common.SolrException\",\n      \"root-error-class\",\"java.lang.OutOfMemoryError\"],\n    \"msg\":\"Exception occured during uninverting text\",\n    \"trace\":\"org.apache.solr.common.SolrException: Exception occured during uninverting text\\n\\tat org.apache.solr.search.facet.UnInvertedField.rethrowAsSolrException(UnInvertedField.java:681)\\n\\tat org.apache.solr.search.facet.UnInvertedField.getUnInvertedField(UnInvertedField.java:621)\\n\\tat org.apache.solr.search.facet.FacetFieldProcessorByArrayUIF.findStartAndEndOrds(FacetFieldProcessorByArrayUIF.java:43)\\n\\tat org.apache.solr.search.facet.FacetFieldProcessorByArray.calcFacets(FacetFieldProcessorByArray.java:116)\\n\\tat org.apache.solr.search.facet.FacetFieldProcessorByArray.process(FacetFieldProcessorByArray.java:94)\\n\\tat org.apache.solr.search.facet.FacetRequest.process(FacetRequest.java:454)\\n\\tat org.apache.solr.search.facet.FacetProcessor.processSubs(FacetProcessor.java:477)\\n\\tat org.apache.solr.search.facet.FacetProcessor.fillBucket(FacetProcessor.java:433)\\n\\tat org.apache.solr.search.facet.FacetQueryProcessor.process(FacetQuery.java:65)\\n\\tat org.apache.solr.search.facet.FacetRequest.process(FacetRequest.java:454)\\n\\tat org.apache.solr.search.facet.FacetModule.process(FacetModule.java:150)\\n\\tat org.apache.solr.handler.component.SearchHandler.handleRequestBody(SearchHandler.java:360)\\n\\tat org.apache.solr.handler.RequestHandlerBase.handleRequest(RequestHandlerBase.java:214)\\n\\tat org.apache.solr.core.SolrCore.execute(SolrCore.java:2627)\\n\\tat org.apache.solr.servlet.HttpSolrCall.execute(HttpSolrCall.java:795)\\n\\tat org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:568)\\n\\tat org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:415)\\n\\tat org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:345)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1596)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:545)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:590)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1610)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1300)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:485)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1580)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1215)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:221)\\n\\tat org.eclipse.jetty.server.handler.InetAccessHandler.handle(InetAccessHandler.java:177)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\\tat org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:322)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:500)\\n\\tat org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)\\n\\tat org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:135)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)\\n\\tat java.base\/java.lang.Thread.run(Thread.java:832)\\nCaused by: java.lang.OutOfMemoryError: Java heap space\\n\\tat org.apache.solr.search.facet.UnInvertedField.visitTerm(UnInvertedField.java:136)\\n\\tat org.apache.solr.uninverting.DocTermOrds.uninvert(DocTermOrds.java:350)\\n\\tat org.apache.solr.search.facet.UnInvertedField.&lt;init>(UnInvertedField.java:205)\\n\\tat org.apache.solr.search.facet.UnInvertedField.lambda$getUnInvertedField$1(UnInvertedField.java:613)\\n\\tat org.apache.solr.search.facet.UnInvertedField$$Lambda$658\/0x000000080116cc40.apply(Unknown Source)\\n\\tat org.apache.solr.util.ConcurrentLRUCache.lambda$computeIfAbsent$1(ConcurrentLRUCache.java:227)\\n\\tat org.apache.solr.util.ConcurrentLRUCache$$Lambda$659\/0x000000080116c040.apply(Unknown Source)\\n\\tat java.base\/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)\\n\\tat org.apache.solr.util.ConcurrentLRUCache.computeIfAbsent(ConcurrentLRUCache.java:226)\\n\\tat org.apache.solr.search.FastLRUCache.computeIfAbsent(FastLRUCache.java:258)\\n\\tat org.apache.solr.search.facet.UnInvertedField.getUnInvertedField(UnInvertedField.java:610)\\n\\tat org.apache.solr.search.facet.FacetFieldProcessorByArrayUIF.findStartAndEndOrds(FacetFieldProcessorByArrayUIF.java:43)\\n\\tat org.apache.solr.search.facet.FacetFieldProcessorByArray.calcFacets(FacetFieldProcessorByArray.java:116)\\n\\tat org.apache.solr.search.facet.FacetFieldProcessorByArray.process(FacetFieldProcessorByArray.java:94)\\n\\tat org.apache.solr.search.facet.FacetRequest.process(FacetRequest.java:454)\\n\\tat org.apache.solr.search.facet.FacetProcessor.processSubs(FacetProcessor.java:477)\\n\\tat org.apache.solr.search.facet.FacetProcessor.fillBucket(FacetProcessor.java:433)\\n\\tat org.apache.solr.search.facet.FacetQueryProcessor.process(FacetQuery.java:65)\\n\\tat org.apache.solr.search.facet.FacetRequest.process(FacetRequest.java:454)\\n\\tat org.apache.solr.search.facet.FacetModule.process(FacetModule.java:150)\\n\\tat org.apache.solr.handler.component.SearchHandler.handleRequestBody(SearchHandler.java:360)\\n\\tat org.apache.solr.handler.RequestHandlerBase.handleRequest(RequestHandlerBase.java:214)\\n\\tat org.apache.solr.core.SolrCore.execute(SolrCore.java:2627)\\n\\tat org.apache.solr.servlet.HttpSolrCall.execute(HttpSolrCall.java:795)\\n\\tat org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:568)\\n\\tat org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:415)\\n\\tat org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:345)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1596)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:545)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:590)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\",\n    \"code\":500}}<\/code><\/pre>\n\n\n\n<p>You can see &#8211; a java.lang.OutOfMemoryError is the root cause. This is an extreme example, but you should be aware that Solr looks at the JVM memory usage to compare the usage with the configuration of the circuit breaker. And that happened because the heap usage of the JVM&nbsp;<strong>didn&#8217;t cross<\/strong>&nbsp;the defined&nbsp;<strong>80%<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/solr.pl\/wp-content\/uploads\/2020\/11\/solr_circuit_breaker-1024x324.png\" alt=\"\" class=\"wp-image-5003\"\/><\/figure>\n\n\n\n<p>Basically, at the time when the limits were checked in Solr, the JVM memory usage was reporting close to <strong>66%<\/strong>, so below <strong>80%<\/strong> and that&#8217;s why the request was allowed to be executed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>Even though not everything is perfect in the Solr circuit breakers world I think this is a step in the right direction. We got the CPU and JVM memory-based ones with the groundwork done for other implementations to follow. Hopefully, in the not too distant future, we will see more and more circuit breaker implementations &#8211; for example, ones limited and dedicated to certain features, like query, indexing, or faceting one. Would you like to see such implementations?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>With the Solr 8.7 release, we were given a very useful feature called circuit breakers. A circuit breaker design pattern allows stopping execution when certain criteria are met. For example, when the memory usage is higher than defined a query<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[27],"tags":[642,640,643,641,639,164],"class_list":["post-1048","post","type-post","status-publish","format-standard","hentry","category-solr-en","tag-breaker-2","tag-circuit-2","tag-circuit-breaker-2","tag-cpu-2","tag-memory-2","tag-solr-2"],"_links":{"self":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts\/1048","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/comments?post=1048"}],"version-history":[{"count":2,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts\/1048\/revisions"}],"predecessor-version":[{"id":1051,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/posts\/1048\/revisions\/1051"}],"wp:attachment":[{"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/media?parent=1048"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/categories?post=1048"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solr.pl\/en\/wp-json\/wp\/v2\/tags?post=1048"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}