{"id":11706,"date":"2022-09-21T09:48:30","date_gmt":"2022-09-21T09:48:30","guid":{"rendered":"https:\/\/predictly.se\/error-handling-with-google-cloud-pubsub\/"},"modified":"2024-05-08T11:46:45","modified_gmt":"2024-05-08T11:46:45","slug":"error-handling-with-google-cloud-pubsub","status":"publish","type":"post","link":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/","title":{"rendered":"Error handling with Google Cloud PubSub"},"content":{"rendered":"<p>We adopted Google Cloud PubSub as our messaging platform at the start of 2020. At the time, it came from the Google Cloud BigData solution and it was obvious that it was tailored to those scenarios. One of the challenges we initially faced was dealing with errors during message processing; thanks to a new release from Google, this is now supported out of the box.<\/p>\n<h6>Data availability with PubSub<\/h6>\n<p>Our architecture is built around the premise that data should always be available for anyone to innovate on. As such we have decided to use <a href=\"https:\/\/cloud.google.com\/pubsub\">GCP PubSub<\/a> as one key part of our data platform; whenever a data element is created or modified it is published for others to consume and react on.<\/p>\n<p>Typically, this flow would work something like this<\/p>\n<p><img class=\"lazyload\" decoding=\"async\" src=\"data:image\/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27736%27%20height%3D%27429%27%20viewBox%3D%270%200%20736%20429%27%3E%3Crect%20width%3D%27736%27%20height%3D%27429%27%20fill-opacity%3D%220%22%2F%3E%3C%2Fsvg%3E\" data-orig-src=\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/felhantering_pubsub-1.png\" alt=\"Flow PubSub\"><\/p>\n<p>For details on this flow, see the PubSub <a href=\"https:\/\/cloud.google.com\/pubsub\/docs\/overview\">documentation<\/a>. The challenging step was #5 where the Subscriber sends an Acknowledgement back to PubSub, telling it that the message has been successfully processed.<\/p>\n<h6>Indefinite Delivery &#8211; Bounce Storms<\/h6>\n<p>Early on we discovered that if the Acknowledgement is not sent, the message will just bounce back and forth between the Subscription and the Subscriber, indefinitely. This was a real set back but something we could overcome by adding code to our Subscriber for proper error handling. Coming from a background working a lot with JMS and Apache ActiveMQ, we were looking for something similar to Dead Letter handling.<\/p>\n<p>At this point, most of our Subscribers grew a lot in lines of code, they had to detect and handle errors and each microservice had to do dead letter handling all by itself.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n@Override\nprotected void consume(BasicAcknowledgeablePubsubMessage basicAcknowledgeablePubsubMessage) {\n  String data = null;\n  Map&lt;String, String&gt; headers = null;\n  try {\n  data = basicAcknowledgeablePubsubMessage.getPubsubMessage().getData().toStringUtf8();\n  headers = basicAcknowledgeablePubsubMessage.getPubsubMessage().getAttributesMap();\n\n  if (correctRecipient(headers.get(HEADER_RECIPIENT), SERVICE_NAME)) {\n  Product p = pmService.parseProduct(data);\n\n  String requestId = !StringUtils.isEmpty(headers.get(HEADER_REQUESTID)) ? headers.get(HEADER_REQUESTID) : UUID.randomUUID().toString(); \/\/ for now, this will always be a random UUID\n  LocalDateTime modifiedTs = null;\n  if (headers != null &amp;&amp; headers.containsKey(HEADER_MODIFIEDTS)) {\n  modifiedTs = LocalDateTime.parse(headers.get(HEADER_MODIFIEDTS), MODIFIEDTS_PARSER);\n  }\n\n  pmService.processProduct(p, modifiedTs, requestId);\n  }\n  } catch(RuntimeException | JsonProcessingException e) {\n  log.error(\"Could not receive message\", e);\n  \/\/ nack only bounces the message, we need to decide if it is transient or infra or something wrong with the message\n  \/\/ otherwise we will get a (re)bounce storm\n  \/\/ send to an error \/ retry queue\n  if ( ENABLE_GCP_DLQ ) {\n  publisher.send(PRODUCT_ERROR_TOPIC, data, headers != null ? headers : Collections.emptyMap());\n  } else {\n  log.error(\"Could not parse message: {}\", data);\n  }\n  } finally {\n  doBlocking(basicAcknowledgeablePubsubMessage.ack());\n  }\n}\n<\/pre>\n<p>Another issue with this setup was that every for every subscriber, we need an additional error topic and an additonal subscriber on the error topic so that our messages are not lost.<\/p>\n<p>The final issue with this setup was that retries was never implemented by us, issues that are intermittent (e.g. redeploy of another service) would automatically cause the message to be sent to the DLQ.<\/p>\n<h6>Support for dead lettering and retries<\/h6>\n<p>Thanks to the latest release of GCP PubSub, we now have support for dead lettering and retries out of the box. Using <a href=\"https:\/\/cloud.google.com\/config-connector\/docs\/overview\">config-connector<\/a>, we can configure our applications as this<\/p>\n<pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\n---\napiVersion: pubsub.cnrm.cloud.google.com\/v1beta1\nchild: PubSubSubscription\nmetadata:\n  labels:\n  {{- include \"springboot.labels\" . | nindent 4 }}\n  name: product-topic-product-service\nspec:\n  ackDeadlineSeconds: 300\n  messageRetentionDuration: 604800s\n  retainAckedMessages: false\n  topicRef:\n  name: product-topic\n  deadLetterPolicy:\n  deadLetterTopicRef:\n  name: global-dlq\n  maxDeliveryAttempts: 5\n  retryPolicy:\n  minimumBackoff: 10s\n  maximumBackoff: 600s\n---\napiVersion: pubsub.cnrm.cloud.google.com\/v1beta1\nchild: PubSubSubscription\nmetadata:\n  labels:\n  {{- include \"springboot.labels\" . | nindent 4 }}\n  name: product-topic-product-service-error-subscriber\nspec:\n  ackDeadlineSeconds: 15\n  messageRetentionDuration: 604800s\n  retainAckedMessages: false\n  topicRef:\n  name: global-dlq\n  filter: attributes.CloudPubSubDeadLetterSourceSubscription = \"product-topic-product-service\"\n<\/pre>\n<h6>PubSub Dead Letter attributes<\/h6>\n<p>When PubSub detects that our message has been retried more than the configured <em>maxDeliveryAttempts<\/em>, it will send the message to <em>deadLetterTopicRef<\/em>. Besides from just sending the message to the dead letter topic, it will add a few attributes to the message as well<\/p>\n<ul>\n<li>CloudPubSubDeadLetterSourceDeliveryCount<\/li>\n<li>CloudPubSubDeadLetterSourceSubscription<\/li>\n<li>CloudPubSubDeadLetterSourceSubscriptionProject<\/li>\n<li>CloudPubSubDeadLetterSourceTopicPublishTime<\/li>\n<\/ul>\n<p>Using these attributes, we can add a <a href=\"https:\/\/cloud.google.com\/pubsub\/docs\/filtering\">filter<\/a> to our error subscriber that will host the broken messages from <em>product-service<\/em>. Our applications now only need to worry about setting up their retry and dead letter configuration and decide if the should send an <em>ack(<\/em> ) or a <em>nack(<\/em> ) back to GCP PubSub, reducing the complexity and risk of manually configured errors.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n@Override\nprotected void consume(BasicAcknowledgeablePubsubMessage msg) {\n  String data = null;\n  Map&lt;String, String&gt; headers = null;\n  try {\n  data = basicAcknowledgeablePubsubMessage.getPubsubMessage().getData().toStringUtf8();\n  headers = basicAcknowledgeablePubsubMessage.getPubsubMessage().getAttributesMap();\n\n  if (correctRecipient(headers.get(HEADER_RECIPIENT), SERVICE_NAME)) {\n  Product p = pmService.parseProduct(data);\n\n  String requestId = !StringUtils.isEmpty(headers.get(HEADER_REQUESTID)) ? headers.get(HEADER_REQUESTID) : UUID.randomUUID().toString(); \/\/ for now, this will always be a random UUID\n  LocalDateTime modifiedTs = null;\n  if (headers != null &amp;&amp; headers.containsKey(HEADER_MODIFIEDTS)) {\n  modifiedTs = LocalDateTime.parse(headers.get(HEADER_MODIFIEDTS), MODIFIEDTS_PARSER);\n  }\n\n  pmService.processProduct(p, modifiedTs, requestId);\n  basicAcknowledgeablePubsubMessage.ack()\n  }\n  } catch(RuntimeException | JsonProcessingException e) {\n  log.error(\"Could not receive message\", e);\n  basicAcknowledgeablePubsubMessage.nack()\n  }\n}\n<\/pre>\n<h6>Summary<\/h6>\n<p>Using the latest features of GCP PubSub it is now possible to have proper retry management, dead letter handling of broken messages and most importantly to reduce the complexity of our Subscription microservices. This makes GCP PubSub much more suitable to use in traditional application messaging.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Google Cloud PubSub supports automatic Retries and Dead Letter handling which makes it much more competent for messaging in applications<\/p>\n","protected":false},"author":6,"featured_media":11464,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"cybocfi_hide_featured_image":"","footnotes":""},"categories":[120,106,119,126],"tags":[],"class_list":["post-11706","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application","category-google-cloud-en","category-java-en","category-pubsub-en"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Error handling with Google Cloud PubSub &#8211; Predictly<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Error handling with Google Cloud PubSub &#8211; Predictly\" \/>\n<meta property=\"og:description\" content=\"Google Cloud PubSub supports automatic Retries and Dead Letter handling which makes it much more competent for messaging in applications\" \/>\n<meta property=\"og:url\" content=\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\" \/>\n<meta property=\"og:site_name\" content=\"Predictly\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/predictly.se\" \/>\n<meta property=\"article:published_time\" content=\"2022-09-21T09:48:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-05-08T11:46:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1429\" \/>\n\t<meta property=\"og:image:height\" content=\"888\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Patrik H\u00f6rlin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@predictly_se\" \/>\n<meta name=\"twitter:site\" content=\"@predictly_se\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Patrik H\u00f6rlin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\"},\"author\":{\"name\":\"Patrik H\u00f6rlin\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3\"},\"headline\":\"Error handling with Google Cloud PubSub\",\"datePublished\":\"2022-09-21T09:48:30+00:00\",\"dateModified\":\"2024-05-08T11:46:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\"},\"wordCount\":878,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/predictly.se\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png\",\"articleSection\":[\"Application\",\"Google Cloud\",\"Java\",\"PubSub\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\",\"url\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\",\"name\":\"Error handling with Google Cloud PubSub &#8211; Predictly\",\"isPartOf\":{\"@id\":\"https:\/\/predictly.se\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png\",\"datePublished\":\"2022-09-21T09:48:30+00:00\",\"dateModified\":\"2024-05-08T11:46:45+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage\",\"url\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png\",\"contentUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png\",\"width\":1429,\"height\":888},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/predictly.se\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Okategoriserad\",\"item\":\"https:\/\/predictly.se\/en\/insikter\/okategoriserad\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Error handling with Google Cloud PubSub\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/predictly.se\/en\/#website\",\"url\":\"https:\/\/predictly.se\/en\/\",\"name\":\"Predictly\",\"description\":\"Professional IT services\",\"publisher\":{\"@id\":\"https:\/\/predictly.se\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/predictly.se\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/predictly.se\/en\/#organization\",\"name\":\"Predictly\",\"url\":\"https:\/\/predictly.se\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg\",\"contentUrl\":\"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg\",\"width\":532,\"height\":96,\"caption\":\"Predictly\"},\"image\":{\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/predictly.se\",\"https:\/\/x.com\/predictly_se\",\"https:\/\/www.linkedin.com\/company\/predictly\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3\",\"name\":\"Patrik H\u00f6rlin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/predictly.se\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g\",\"caption\":\"Patrik H\u00f6rlin\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Error handling with Google Cloud PubSub &#8211; Predictly","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/","og_locale":"en_US","og_type":"article","og_title":"Error handling with Google Cloud PubSub &#8211; Predictly","og_description":"Google Cloud PubSub supports automatic Retries and Dead Letter handling which makes it much more competent for messaging in applications","og_url":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/","og_site_name":"Predictly","article_publisher":"https:\/\/www.facebook.com\/predictly.se","article_published_time":"2022-09-21T09:48:30+00:00","article_modified_time":"2024-05-08T11:46:45+00:00","og_image":[{"width":1429,"height":888,"url":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png","type":"image\/png"}],"author":"Patrik H\u00f6rlin","twitter_card":"summary_large_image","twitter_creator":"@predictly_se","twitter_site":"@predictly_se","twitter_misc":{"Written by":"Patrik H\u00f6rlin","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#article","isPartOf":{"@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/"},"author":{"name":"Patrik H\u00f6rlin","@id":"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3"},"headline":"Error handling with Google Cloud PubSub","datePublished":"2022-09-21T09:48:30+00:00","dateModified":"2024-05-08T11:46:45+00:00","mainEntityOfPage":{"@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/"},"wordCount":878,"commentCount":0,"publisher":{"@id":"https:\/\/predictly.se\/en\/#organization"},"image":{"@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage"},"thumbnailUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png","articleSection":["Application","Google Cloud","Java","PubSub"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/","url":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/","name":"Error handling with Google Cloud PubSub &#8211; Predictly","isPartOf":{"@id":"https:\/\/predictly.se\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage"},"image":{"@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage"},"thumbnailUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png","datePublished":"2022-09-21T09:48:30+00:00","dateModified":"2024-05-08T11:46:45+00:00","breadcrumb":{"@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#primaryimage","url":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png","contentUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/09\/bottle.png","width":1429,"height":888},{"@type":"BreadcrumbList","@id":"https:\/\/predictly.se\/en\/error-handling-with-google-cloud-pubsub\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/predictly.se\/en\/"},{"@type":"ListItem","position":2,"name":"Okategoriserad","item":"https:\/\/predictly.se\/en\/insikter\/okategoriserad\/"},{"@type":"ListItem","position":3,"name":"Error handling with Google Cloud PubSub"}]},{"@type":"WebSite","@id":"https:\/\/predictly.se\/en\/#website","url":"https:\/\/predictly.se\/en\/","name":"Predictly","description":"Professional IT services","publisher":{"@id":"https:\/\/predictly.se\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/predictly.se\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/predictly.se\/en\/#organization","name":"Predictly","url":"https:\/\/predictly.se\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/","url":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg","contentUrl":"https:\/\/predictly.se\/wp-content\/uploads\/2022\/10\/Logotype1-mobil.svg","width":532,"height":96,"caption":"Predictly"},"image":{"@id":"https:\/\/predictly.se\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/predictly.se","https:\/\/x.com\/predictly_se","https:\/\/www.linkedin.com\/company\/predictly\/"]},{"@type":"Person","@id":"https:\/\/predictly.se\/en\/#\/schema\/person\/57c5342be26b4569def7315855fa23c3","name":"Patrik H\u00f6rlin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/predictly.se\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/60d93834b6a9c87c5625621e882e4a7c538383fb9323297804eb2473d122e9c8?s=96&d=mm&r=g","caption":"Patrik H\u00f6rlin"}}]}},"_links":{"self":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts\/11706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/comments?post=11706"}],"version-history":[{"count":1,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts\/11706\/revisions"}],"predecessor-version":[{"id":11718,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/posts\/11706\/revisions\/11718"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/media\/11464"}],"wp:attachment":[{"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/media?parent=11706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/categories?post=11706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/predictly.se\/en\/wp-json\/wp\/v2\/tags?post=11706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}