{"action":"created","comment":{"url":"https://api.github.com/repos/graknlabs/grakn/pulls/comments/284827892","pull_request_review_id":238547457,"id":284827892,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDI4NDgyNzg5Mg==","diff_hunk":"@@ -0,0 +1,84 @@\n+/*\n+ * GRAKN.AI - THE KNOWLEDGE GRAPH\n+ * Copyright (C) 2018 Grakn Labs Ltd\n+ *\n+ * This program is free software: you can redistribute it and/or modify\n+ * it under the terms of the GNU Affero General Public License as\n+ * published by the Free Software Foundation, either version 3 of the\n+ * License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU Affero General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Affero General Public License\n+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.\n+ */\n+\n+package grakn.core.server.statistics;\n+\n+import grakn.core.concept.Concept;\n+import grakn.core.concept.Label;\n+import grakn.core.server.kb.Schema;\n+import grakn.core.server.kb.concept.ConceptVertex;\n+import grakn.core.server.session.TransactionOLTP;\n+import org.apache.tinkerpop.gremlin.structure.Vertex;\n+import org.apache.tinkerpop.gremlin.structure.VertexProperty;\n+\n+import java.util.HashMap;\n+import java.util.Map;\n+import java.util.concurrent.ConcurrentHashMap;\n+\n+public class KeyspaceStatistics {\n+\n+    private ConcurrentHashMap<String, Long> instanceCountsCache;\n+\n+    public KeyspaceStatistics() {\n+        instanceCountsCache = new ConcurrentHashMap<>();\n+    }\n+\n+    public long count(TransactionOLTP tx, String label) {\n+        // return count if cached, else cache miss and retrieve from Janus\n+        instanceCountsCache.computeIfAbsent(label, l -> retrieveCountFromVertex(tx, l));\n+        return instanceCountsCache.get(label);\n+    }\n+\n+    public void commit(TransactionOLTP tx, UncomittedStatisticsDelta statisticsDelta) {\n+        HashMap<String, Long> deltaMap = statisticsDelta.instanceDeltas();\n+\n+        // merge each delta into the cache, then flush the cache to Janus\n+        for (Map.Entry<String, Long> entry : deltaMap.entrySet()) {\n+            String label = entry.getKey();\n+            Long delta = entry.getValue();\n+            instanceCountsCache.compute(label, (k, prior) -> {\n+                long existingCount;\n+                if (instanceCountsCache.containsKey(label)) {\n+                    existingCount = prior;\n+                } else {\n+                    existingCount = retrieveCountFromVertex(tx, label) ;\n+                }\n+                return existingCount + delta;\n+            });\n+        }\n+\n+        persist(tx);\n+    }\n+\n+    private void persist(TransactionOLTP tx) {\n+        for (String label : instanceCountsCache.keySet()) {\n+            Long count = instanceCountsCache.get(label);\n+            Concept schemaConcept = tx.getSchemaConcept(Label.of(label));","path":"server/src/server/statistics/KeyspaceStatistics.java","position":71,"original_position":71,"commit_id":"1c54f0f1de6e55c416f65ef1d2c1f6d96dc087e5","original_commit_id":"1c54f0f1de6e55c416f65ef1d2c1f6d96dc087e5","user":{"login":"kasper-piskorski","id":10519067,"node_id":"MDQ6VXNlcjEwNTE5MDY3","avatar_url":"https://avatars2.githubusercontent.com/u/10519067?v=4","gravatar_id":"","url":"https://api.github.com/users/kasper-piskorski","html_url":"https://github.com/kasper-piskorski","followers_url":"https://api.github.com/users/kasper-piskorski/followers","following_url":"https://api.github.com/users/kasper-piskorski/following{/other_user}","gists_url":"https://api.github.com/users/kasper-piskorski/gists{/gist_id}","starred_url":"https://api.github.com/users/kasper-piskorski/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kasper-piskorski/subscriptions","organizations_url":"https://api.github.com/users/kasper-piskorski/orgs","repos_url":"https://api.github.com/users/kasper-piskorski/repos","events_url":"https://api.github.com/users/kasper-piskorski/events{/privacy}","received_events_url":"https://api.github.com/users/kasper-piskorski/received_events","type":"User","site_admin":false},"body":"Maybe do a null check here? What if we remove a type that has a key label in the cache?","created_at":"2019-05-16T17:56:19Z","updated_at":"2019-05-16T17:57:50Z","html_url":"https://github.com/graknlabs/grakn/pull/5198#discussion_r284827892","pull_request_url":"https://api.github.com/repos/graknlabs/grakn/pulls/5198","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/graknlabs/grakn/pulls/comments/284827892"},"html":{"href":"https://github.com/graknlabs/grakn/pull/5198#discussion_r284827892"},"pull_request":{"href":"https://api.github.com/repos/graknlabs/grakn/pulls/5198"}}},"pull_request":{"url":"https://api.github.com/repos/graknlabs/grakn/pulls/5198","id":279577279,"node_id":"MDExOlB1bGxSZXF1ZXN0Mjc5NTc3Mjc5","html_url":"https://github.com/graknlabs/grakn/pull/5198","diff_url":"https://github.com/graknlabs/grakn/pull/5198.diff","patch_url":"https://github.com/graknlabs/grakn/pull/5198.patch","issue_url":"https://api.github.com/repos/graknlabs/grakn/issues/5198","number":5198,"state":"open","locked":false,"title":"[DO NOT MERGE YET] Statistics 1","user":{"login":"flyingsilverfin","id":1386526,"node_id":"MDQ6VXNlcjEzODY1MjY=","avatar_url":"https://avatars3.githubusercontent.com/u/1386526?v=4","gravatar_id":"","url":"https://api.github.com/users/flyingsilverfin","html_url":"https://github.com/flyingsilverfin","followers_url":"https://api.github.com/users/flyingsilverfin/followers","following_url":"https://api.github.com/users/flyingsilverfin/following{/other_user}","gists_url":"https://api.github.com/users/flyingsilverfin/gists{/gist_id}","starred_url":"https://api.github.com/users/flyingsilverfin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/flyingsilverfin/subscriptions","organizations_url":"https://api.github.com/users/flyingsilverfin/orgs","repos_url":"https://api.github.com/users/flyingsilverfin/repos","events_url":"https://api.github.com/users/flyingsilverfin/events{/privacy}","received_events_url":"https://api.github.com/users/flyingsilverfin/received_events","type":"User","site_admin":false},"body":"## What is the goal of this PR?\r\nImplement persisted statistics that count 0th order statistics - counts of each type. We now are able to query for the count of committed concepts of each type via the `KeyspaceStatistics` object, that is shared across Sessions. Accessing the counts requires a transaction, as the `KeyspaceStatistics` operate as a cache that on miss query from Janus. \r\n\r\nWe also do a first step to using statistics in query planning - eliminating all but the top 3 starting points in a query, if more than 3 \"good\" starting points are available.\r\n\r\n## What are the changes implemented in this PR?\r\n* `KeyspaceStatistics` - read and write (persistently) statistics for a specific keyspace\r\n* `UncomittedStatisticsDelta` - lives within a Transaction and on commit is merged into `KeyspaceStatistics`\r\n* Tests for both of these ensuring each type of concept (including implicit attr relations) are counted correctly, and persisted across openings/closing of non-overlapping sessions to a keyspace.\r\n* `TraversalPlanner.estimateInitialCost` -- provides a heuristic cost - based on number of owners or instances of that type, for the cost of the first step from that fixed cost starting node.\r\n\r\n## Shortcomings\r\n* No awareness of distributed system possibility - caching is done locally. This would need to be addressed to read accurate instance counts in a distributed environment\r\n* The `estimateInitialCost` method portion that computes cost of `ValueFragment` is expensive (retrieves all subs of `attribute` and `@has-attribute`) and probably not the best heuristic for the cost -- the first can be solved by storing base type of each concept type as well, providing quick aggregation of the number of entities/attrs/rels without traversing all the subs()","created_at":"2019-05-16T16:13:50Z","updated_at":"2019-05-16T17:57:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1cfcedbde5f123ae3fa1d629167b15d57aec2587","assignee":null,"assignees":[],"requested_reviewers":[{"login":"marco-scoppetta","id":6430514,"node_id":"MDQ6VXNlcjY0MzA1MTQ=","avatar_url":"https://avatars1.githubusercontent.com/u/6430514?v=4","gravatar_id":"","url":"https://api.github.com/users/marco-scoppetta","html_url":"https://github.com/marco-scoppetta","followers_url":"https://api.github.com/users/marco-scoppetta/followers","following_url":"https://api.github.com/users/marco-scoppetta/following{/other_user}","gists_url":"https://api.github.com/users/marco-scoppetta/gists{/gist_id}","starred_url":"https://api.github.com/users/marco-scoppetta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marco-scoppetta/subscriptions","organizations_url":"https://api.github.com/users/marco-scoppetta/orgs","repos_url":"https://api.github.com/users/marco-scoppetta/repos","events_url":"https://api.github.com/users/marco-scoppetta/events{/privacy}","received_events_url":"https://api.github.com/users/marco-scoppetta/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/graknlabs/grakn/pulls/5198/commits","review_comments_url":"https://api.github.com/repos/graknlabs/grakn/pulls/5198/comments","review_comment_url":"https://api.github.com/repos/graknlabs/grakn/pulls/comments{/number}","comments_url":"https://api.github.com/repos/graknlabs/grakn/issues/5198/comments","statuses_url":"https://api.github.com/repos/graknlabs/grakn/statuses/1c54f0f1de6e55c416f65ef1d2c1f6d96dc087e5","head":{"label":"flyingsilverfin:statistics-1","ref":"statistics-1","sha":"1c54f0f1de6e55c416f65ef1d2c1f6d96dc087e5","user":{"login":"flyingsilverfin","id":1386526,"node_id":"MDQ6VXNlcjEzODY1MjY=","avatar_url":"https://avatars3.githubusercontent.com/u/1386526?v=4","gravatar_id":"","url":"https://api.github.com/users/flyingsilverfin","html_url":"https://github.com/flyingsilverfin","followers_url":"https://api.github.com/users/flyingsilverfin/followers","following_url":"https://api.github.com/users/flyingsilverfin/following{/other_user}","gists_url":"https://api.github.com/users/flyingsilverfin/gists{/gist_id}","starred_url":"https://api.github.com/users/flyingsilverfin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/flyingsilverfin/subscriptions","organizations_url":"https://api.github.com/users/flyingsilverfin/orgs","repos_url":"https://api.github.com/users/flyingsilverfin/repos","events_url":"https://api.github.com/users/flyingsilverfin/events{/privacy}","received_events_url":"https://api.github.com/users/flyingsilverfin/received_events","type":"User","site_admin":false},"repo":{"id":141741973,"node_id":"MDEwOlJlcG9zaXRvcnkxNDE3NDE5NzM=","name":"grakn","full_name":"flyingsilverfin/grakn","private":false,"owner":{"login":"flyingsilverfin","id":1386526,"node_id":"MDQ6VXNlcjEzODY1MjY=","avatar_url":"https://avatars3.githubusercontent.com/u/1386526?v=4","gravatar_id":"","url":"https://api.github.com/users/flyingsilverfin","html_url":"https://github.com/flyingsilverfin","followers_url":"https://api.github.com/users/flyingsilverfin/followers","following_url":"https://api.github.com/users/flyingsilverfin/following{/other_user}","gists_url":"https://api.github.com/users/flyingsilverfin/gists{/gist_id}","starred_url":"https://api.github.com/users/flyingsilverfin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/flyingsilverfin/subscriptions","organizations_url":"https://api.github.com/users/flyingsilverfin/orgs","repos_url":"https://api.github.com/users/flyingsilverfin/repos","events_url":"https://api.github.com/users/flyingsilverfin/events{/privacy}","received_events_url":"https://api.github.com/users/flyingsilverfin/received_events","type":"User","site_admin":false},"html_url":"https://github.com/flyingsilverfin/grakn","description":"The Knowledge Graph","fork":true,"url":"https://api.github.com/repos/flyingsilverfin/grakn","forks_url":"https://api.github.com/repos/flyingsilverfin/grakn/forks","keys_url":"https://api.github.com/repos/flyingsilverfin/grakn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/flyingsilverfin/grakn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/flyingsilverfin/grakn/teams","hooks_url":"https://api.github.com/repos/flyingsilverfin/grakn/hooks","issue_events_url":"https://api.github.com/repos/flyingsilverfin/grakn/issues/events{/number}","events_url":"https://api.github.com/repos/flyingsilverfin/grakn/events","assignees_url":"https://api.github.com/repos/flyingsilverfin/grakn/assignees{/user}","branches_url":"https://api.github.com/repos/flyingsilverfin/grakn/branches{/branch}","tags_url":"https://api.github.com/repos/flyingsilverfin/grakn/tags","blobs_url":"https://api.github.com/repos/flyingsilverfin/grakn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/flyingsilverfin/grakn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/flyingsilverfin/grakn/git/refs{/sha}","trees_url":"https://api.github.com/repos/flyingsilverfin/grakn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/flyingsilverfin/grakn/statuses/{sha}","languages_url":"https://api.github.com/repos/flyingsilverfin/grakn/languages","stargazers_url":"https://api.github.com/repos/flyingsilverfin/grakn/stargazers","contributors_url":"https://api.github.com/repos/flyingsilverfin/grakn/contributors","subscribers_url":"https://api.github.com/repos/flyingsilverfin/grakn/subscribers","subscription_url":"https://api.github.com/repos/flyingsilverfin/grakn/subscription","commits_url":"https://api.github.com/repos/flyingsilverfin/grakn/commits{/sha}","git_commits_url":"https://api.github.com/repos/flyingsilverfin/grakn/git/commits{/sha}","comments_url":"https://api.github.com/repos/flyingsilverfin/grakn/comments{/number}","issue_comment_url":"https://api.github.com/repos/flyingsilverfin/grakn/issues/comments{/number}","contents_url":"https://api.github.com/repos/flyingsilverfin/grakn/contents/{+path}","compare_url":"https://api.github.com/repos/flyingsilverfin/grakn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/flyingsilverfin/grakn/merges","archive_url":"https://api.github.com/repos/flyingsilverfin/grakn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/flyingsilverfin/grakn/downloads","issues_url":"https://api.github.com/repos/flyingsilverfin/grakn/issues{/number}","pulls_url":"https://api.github.com/repos/flyingsilverfin/grakn/pulls{/number}","milestones_url":"https://api.github.com/repos/flyingsilverfin/grakn/milestones{/number}","notifications_url":"https://api.github.com/repos/flyingsilverfin/grakn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/flyingsilverfin/grakn/labels{/name}","releases_url":"https://api.github.com/repos/flyingsilverfin/grakn/releases{/id}","deployments_url":"https://api.github.com/repos/flyingsilverfin/grakn/deployments","created_at":"2018-07-20T17:52:12Z","updated_at":"2018-09-24T15:58:02Z","pushed_at":"2019-05-16T17:48:57Z","git_url":"git://github.com/flyingsilverfin/grakn.git","ssh_url":"git@github.com:flyingsilverfin/grakn.git","clone_url":"https://github.com/flyingsilverfin/grakn.git","svn_url":"https://github.com/flyingsilverfin/grakn","homepage":"https://grakn.ai","size":79864,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"graknlabs:master","ref":"master","sha":"5930c3a632840080e51af4a7a111e823ce1b20b6","user":{"login":"graknlabs","id":10490615,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwNDkwNjE1","avatar_url":"https://avatars1.githubusercontent.com/u/10490615?v=4","gravatar_id":"","url":"https://api.github.com/users/graknlabs","html_url":"https://github.com/graknlabs","followers_url":"https://api.github.com/users/graknlabs/followers","following_url":"https://api.github.com/users/graknlabs/following{/other_user}","gists_url":"https://api.github.com/users/graknlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/graknlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/graknlabs/subscriptions","organizations_url":"https://api.github.com/users/graknlabs/orgs","repos_url":"https://api.github.com/users/graknlabs/repos","events_url":"https://api.github.com/users/graknlabs/events{/privacy}","received_events_url":"https://api.github.com/users/graknlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":63110867,"node_id":"MDEwOlJlcG9zaXRvcnk2MzExMDg2Nw==","name":"grakn","full_name":"graknlabs/grakn","private":false,"owner":{"login":"graknlabs","id":10490615,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwNDkwNjE1","avatar_url":"https://avatars1.githubusercontent.com/u/10490615?v=4","gravatar_id":"","url":"https://api.github.com/users/graknlabs","html_url":"https://github.com/graknlabs","followers_url":"https://api.github.com/users/graknlabs/followers","following_url":"https://api.github.com/users/graknlabs/following{/other_user}","gists_url":"https://api.github.com/users/graknlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/graknlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/graknlabs/subscriptions","organizations_url":"https://api.github.com/users/graknlabs/orgs","repos_url":"https://api.github.com/users/graknlabs/repos","events_url":"https://api.github.com/users/graknlabs/events{/privacy}","received_events_url":"https://api.github.com/users/graknlabs/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/graknlabs/grakn","description":"Grakn Core: The Knowledge Graph","fork":false,"url":"https://api.github.com/repos/graknlabs/grakn","forks_url":"https://api.github.com/repos/graknlabs/grakn/forks","keys_url":"https://api.github.com/repos/graknlabs/grakn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/graknlabs/grakn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/graknlabs/grakn/teams","hooks_url":"https://api.github.com/repos/graknlabs/grakn/hooks","issue_events_url":"https://api.github.com/repos/graknlabs/grakn/issues/events{/number}","events_url":"https://api.github.com/repos/graknlabs/grakn/events","assignees_url":"https://api.github.com/repos/graknlabs/grakn/assignees{/user}","branches_url":"https://api.github.com/repos/graknlabs/grakn/branches{/branch}","tags_url":"https://api.github.com/repos/graknlabs/grakn/tags","blobs_url":"https://api.github.com/repos/graknlabs/grakn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/graknlabs/grakn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/graknlabs/grakn/git/refs{/sha}","trees_url":"https://api.github.com/repos/graknlabs/grakn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/graknlabs/grakn/statuses/{sha}","languages_url":"https://api.github.com/repos/graknlabs/grakn/languages","stargazers_url":"https://api.github.com/repos/graknlabs/grakn/stargazers","contributors_url":"https://api.github.com/repos/graknlabs/grakn/contributors","subscribers_url":"https://api.github.com/repos/graknlabs/grakn/subscribers","subscription_url":"https://api.github.com/repos/graknlabs/grakn/subscription","commits_url":"https://api.github.com/repos/graknlabs/grakn/commits{/sha}","git_commits_url":"https://api.github.com/repos/graknlabs/grakn/git/commits{/sha}","comments_url":"https://api.github.com/repos/graknlabs/grakn/comments{/number}","issue_comment_url":"https://api.github.com/repos/graknlabs/grakn/issues/comments{/number}","contents_url":"https://api.github.com/repos/graknlabs/grakn/contents/{+path}","compare_url":"https://api.github.com/repos/graknlabs/grakn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/graknlabs/grakn/merges","archive_url":"https://api.github.com/repos/graknlabs/grakn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/graknlabs/grakn/downloads","issues_url":"https://api.github.com/repos/graknlabs/grakn/issues{/number}","pulls_url":"https://api.github.com/repos/graknlabs/grakn/pulls{/number}","milestones_url":"https://api.github.com/repos/graknlabs/grakn/milestones{/number}","notifications_url":"https://api.github.com/repos/graknlabs/grakn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/graknlabs/grakn/labels{/name}","releases_url":"https://api.github.com/repos/graknlabs/grakn/releases{/id}","deployments_url":"https://api.github.com/repos/graknlabs/grakn/deployments","created_at":"2016-07-11T23:59:57Z","updated_at":"2019-05-16T16:19:16Z","pushed_at":"2019-05-16T17:48:59Z","git_url":"git://github.com/graknlabs/grakn.git","ssh_url":"git@github.com:graknlabs/grakn.git","clone_url":"https://github.com/graknlabs/grakn.git","svn_url":"https://github.com/graknlabs/grakn","homepage":"https://grakn.ai","size":79129,"stargazers_count":1566,"watchers_count":1566,"language":"Java","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":188,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":236,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":188,"open_issues":236,"watchers":1566,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/graknlabs/grakn/pulls/5198"},"html":{"href":"https://github.com/graknlabs/grakn/pull/5198"},"issue":{"href":"https://api.github.com/repos/graknlabs/grakn/issues/5198"},"comments":{"href":"https://api.github.com/repos/graknlabs/grakn/issues/5198/comments"},"review_comments":{"href":"https://api.github.com/repos/graknlabs/grakn/pulls/5198/comments"},"review_comment":{"href":"https://api.github.com/repos/graknlabs/grakn/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/graknlabs/grakn/pulls/5198/commits"},"statuses":{"href":"https://api.github.com/repos/graknlabs/grakn/statuses/1c54f0f1de6e55c416f65ef1d2c1f6d96dc087e5"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"kasper-piskorski"}}