{"id":779,"date":"2009-11-06T17:09:35","date_gmt":"2009-11-06T16:09:35","guid":{"rendered":"http:\/\/www.gamlor.info\/wordpress\/?p=779"},"modified":"2021-03-11T09:44:57","modified_gmt":"2021-03-11T08:44:57","slug":"db4o-client-server-and-concurrency","status":"publish","type":"post","link":"https:\/\/www.gamlor.info\/wordpress\/2009\/11\/db4o-client-server-and-concurrency\/","title":{"rendered":"db4o: Client-Server and Concurrency"},"content":{"rendered":"<p>So far we\u2019ve always used a single object container. This it the simplest way to used db4o. Just open an embedded database an use it. In this post I\u2019ll give a short introduction to the client-server-features of db4o. Handling concurrency is more challenging in a server-client scenario, therefore I also loose a few words about that.<\/p>\n<p>(All posts of this series: <a href=\"?p=620\">the basics<\/a>, <a href=\"?p=637\">activation<\/a>, <a href=\"?p=654\">object-identity<\/a>, <a href=\"?p=671\">transactions<\/a>, <a href=\"?p=695\">persistent classes<\/a>, <a href=\"?p=733\">single container concurrency<\/a>, <a href=\"?p=744\">Queries in Java, C# 2.0<\/a>, <a href=\"?p=779\">client-server concurrency<\/a>, <a href=\"?p=840\">transparent persistence<\/a>, <a href=\"?p=949\">adhoc query tools<\/a>)<\/p>\n<h3>Embedded Client-Server<\/h3>\n<p>Let\u2019s assume we build a small web application. Naturally multiple requests are handled concurrently. Of course you want to run each request independent from other request. Having a transaction for each request would be nice, because then you can rollback the transaction when a request fails. So far we\u2019ve always used a single object-container. Additionally we know that a transaction is bound to a object-container. In order to support our scenario we need more than one object container. No problem, it quite easy:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">const<\/span> <span class=\"kwrd\">int<\/span> RunEmbeddedServer = 0;\r\nvar server = Db4oClientServer.OpenServer(FilePath, RunEmbeddedServer);\r\nvar db1 = server.OpenClient();\r\nDoStuffWith(db1);\r\nvar db2 = server.OpenClient();\r\nDoStuffWith(db2);<\/pre>\n<pre class=\"csharpcode\"><\/pre>\n<p>Creating a new server is really easy. You pass in the database-file, a optional configuration and a port-number to the factory and you\u2019re done. When you pass a 0 as port-number an <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/client-server\/embedded.html\">embedded server<\/a> is created. You can open as many clients as you want on this embedded server with the .OpenClient()-method.<\/p>\n<p>For our small web application this is wonderful solution. We still have an embedded database without additional deployments, administration and so forth. The server just runs within the web application.<\/p>\n<p><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-780\" title=\"db4o-cs\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs.png\" alt=\"db4o-cs\" width=\"600\" height=\"495\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs.png 600w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs-300x247.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<h3>Client-Server Over a Network<\/h3>\n<p>Before we created an embedded client-server. But what if the clients are to be different processes on different devices? Well the step is really small, you take the calls above and specify additionally the port and grant access to users. Now your server is ready to <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/client-server\/networked\/network_server.html\">accept clients over the network<\/a>:<\/p>\n<p>The server:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">const<\/span> <span class=\"kwrd\">int<\/span> RunAsRealServer = 8080;\r\nvar server = Db4oClientServer.OpenServer(FilePath, RunAsRealServer);\r\nserver.GrantAccess(<span class=\"str\">\"db4o\"<\/span>,<span class=\"str\">\"passwordOfUser\"<\/span>);<\/pre>\n<p>The client:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">using<\/span>(var db = Db4oClientServer.OpenClient(<span class=\"str\">\"localhost\"<\/span>, 0, <span class=\"str\">\"db4o\"<\/span>, <span class=\"str\">\"passwordOfUser\"<\/span>))\r\n{\r\n    DoStuffWith(db);\r\n}<\/pre>\n<pre class=\"csharpcode\"><\/pre>\n<p>Opening a server and connecting clients of the network isn\u2019t much more work. The .OpenClient()-method on the server still works. So the server-process can open internal clients, for example to execute maintenance-work. Of course opening a connection over the network has way more overhead than opening embedded clients.<\/p>\n<p><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs-network.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-781\" title=\"db4o-cs-network\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs-network.png\" alt=\"db4o-cs-network\" width=\"600\" height=\"494\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs-network.png 600w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cs-network-300x247.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<h3>Server With \/ Without Classes<\/h3>\n<p>Now running the server and clients in different process brings a whole new complexity with it. Remember that the classes actually is the schema of your database? I also said that a query might cannot be optimized and the actual objects are instantiated to run the queries. That\u2019s why there\u2019s a big difference to have a server which has all persisted classes and a server which only runs a pure db4o instance. A server without persisted <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/client-server\/server_without_persistent_classes_deployed.html\">classes cannot execute all kinds of queries<\/a>. Here I think it shows that db4o\u2019s focuses on embedded scenarios.<\/p>\n<div id=\"attachment_782\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-with-or-without-classes.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-782\" class=\"size-medium wp-image-782\" title=\"db4o-with-or-without-classes\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-with-or-without-classes-300x152.png\" alt=\"db4o without or with persistet classes\" width=\"300\" height=\"152\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-with-or-without-classes-300x152.png 300w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-with-or-without-classes-1024x520.png 1024w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-with-or-without-classes.png 1100w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-782\" class=\"wp-caption-text\">db4o without or with persistet classes<\/p><\/div>\n<h3>Isolation Between Clients<\/h3>\n<p>First lets check something very simple. In the <a href=\"?p=654\">post about object-identity<\/a> I said that object-container keeps references to the loaded objects. It\u2019s used to keep track of identity and for caching purposes. So how does this work in client-server-mode when multiple object-containers are around? To check this we load the same object from different object-containers. Then we check if the objects are the same by identity:<\/p>\n<pre class=\"csharpcode\">var client1 = server.OpenClient();\r\nvar client2 = server.OpenClient();\r\nvar objFromClient1 = (from SimpleObject o <span class=\"kwrd\">in<\/span> client1\r\n                         <span class=\"kwrd\">where<\/span> o.Name == <span class=\"str\">\"first-Obj\"<\/span>\r\n                         select o).Single();\r\nvar objFromClient2 = (from SimpleObject o <span class=\"kwrd\">in<\/span> client2\r\n                         <span class=\"kwrd\">where<\/span> o.Name == <span class=\"str\">\"first-Obj\"<\/span>\r\n                         select o).Single();\r\nAssertTrue(ReferenceEquals(objFromClient1, objFromClient2),<span class=\"str\">\"Have a shared reference-cache\"<\/span>);<\/pre>\n<pre class=\"csharpcode\"><\/pre>\n<p>The assert will fail! Because each client object-container has its own reference-tracking. Basically a client object-container acts the same as a simple object-container. When you have multiple object-containers in your application, it\u2019s extremely important that objects are always loaded and stored with the same object-container instance. Otherwise you run into the magic-clone issue I\u2019ve described in the <a href=\"?p=654\">object-identity-post<\/a>.<\/p>\n<p>Now to the next big question. When is a change from one object-container visible to the other object-container? As already mentioned in the <a href=\"?p=671\">transaction-post<\/a>, db4o has <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/basic_concepts\/acid_model\/isolation_level_for_db4o.html\">read-committed isolation properties<\/a>. Here\u2019s a little example to illustrate this. We open two client object-containers. The first client stores some object in the database. When the second client queries the database it doesn\u2019t see the stored objects, until the first client commits its changes:<\/p>\n<pre class=\"csharpcode\">var client1 = server.OpenClient();\r\nvar client2 = server.OpenClient();\r\n\r\nclient1.Store(<span class=\"kwrd\">new<\/span> SimpleObject(<span class=\"str\">\"first-Obj\"<\/span>,2,2));\r\nclient1.Store(<span class=\"kwrd\">new<\/span> SimpleObject(<span class=\"str\">\"first-Obj\"<\/span>,3,3));\r\n\r\nvar countBeforeCommit = (from SimpleObject o <span class=\"kwrd\">in<\/span> client2\r\n                         select o).Count();\r\nAssertTrue(0 == countBeforeCommit,<span class=\"str\">\"Doesn't see stuff from first client\"<\/span>);\r\n\r\nclient1.Commit();\r\n\r\nvar countAfterCommit = (from SimpleObject o <span class=\"kwrd\">in<\/span> client2\r\n                        select o).Count();\r\nAssertTrue(2 == countAfterCommit,<span class=\"str\">\"After commit it does see the stuff from the first client\"<\/span>);<\/pre>\n<p><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-isolation1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-784\" title=\"db4o-isolation\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-isolation1.png\" alt=\"db4o-isolation\" width=\"600\" height=\"252\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-isolation1.png 600w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-isolation1-300x126.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<h3>Beware Of Cached Instances<\/h3>\n<p>Ok, the object-containers are well isolated and changes are visible after commits. Ready for more? Let\u2019s take a look at the example code below. Again we open two client object-containers. Each get&#8217;s the object by its name. Then the first client changes the name, stores and commits the changes. After that we get the object by its new name on the second client object-container. Then we compare the names. Will it run fine?<\/p>\n<pre class=\"csharpcode\">var client1 = server.OpenClient();\r\nvar client2 = server.OpenClient();\r\nvar objFromClient1 = LoadObjectByName(client1, <span class=\"str\">\"old-name\"<\/span>);\r\nvar objFromClient2 = LoadObjectByName(client2, <span class=\"str\">\"old-name\"<\/span>);\r\nAssertTrue(objFromClient1.Name==objFromClient2.Name,<span class=\"str\">\"Names are equal\"<\/span>);\r\n\r\nobjFromClient1.Name = <span class=\"str\">\"new-name\"<\/span>;\r\nclient1.Store(objFromClient1);\r\nclient1.Commit();\r\n\r\nobjFromClient2 = LoadObjectByName(client2, <span class=\"str\">\"new-name\"<\/span>);\r\nAssertTrue(<span class=\"str\">\"new-name\"<\/span> == objFromClient2.Name, <span class=\"str\">\"Names are equal\"<\/span>);<\/pre>\n<p>Well the result is probably unexpected. The second assertion fails. What the fuck? We ran the query with the \u2018new-name\u2019 and retrieved the object. But the object actually has the \u2018old-name\u2019? That\u2019s totally inconsistent! Well here the reference-cache plays a dirty trick. As said, db4o holds references to already loaded object and only returns the instantiated object to avoid unnecessary de-serialization. Because the second client object-container already has a instance of the object it simple returns the existing instance. Unfortunatetly the object in memory doesn\u2019t have the same state as the object in the database. This is a very common issue with all database-systems. You can explicitly refresh an object. This will read set a object in memory to the state in the database:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">const<\/span> <span class=\"kwrd\">int<\/span> activationDeph = 4;\r\nclient2.Ext().Refresh(objFromClient2, activationDeph);<\/pre>\n<p>Or you open for each task a new client object-container. In the embedded client-server mode this is quite a good solution, since its cheap to open a new client. Over a network its rather a costly operation.<\/p>\n<p>However in the end you\u2019ve always a race condition, because another client could do something between the refresh and the store-operation.<\/p>\n<div id=\"attachment_785\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cached-objects.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-785\" class=\"size-medium wp-image-785\" title=\"db4o-cached-objects\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cached-objects-300x121.png\" alt=\"cached objects\" width=\"300\" height=\"121\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cached-objects-300x121.png 300w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-cached-objects.png 1024w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-785\" class=\"wp-caption-text\">cached objects<\/p><\/div>\n<h3>Locking<\/h3>\n<p>As seen above db4o hasn\u2019t a very \u2018strict\u2019 concurrency model. Therefore you might need more control. For this purpose db4o provides <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/client-server\/semaphores.html\">low-level locks<\/a>. The API is simple:<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">const<\/span> <span class=\"kwrd\">int<\/span> timeOutInMilliSec = 1000;\r\n<span class=\"kwrd\">try<\/span>\r\n{\r\n    objContainer.Ext().SetSemaphore(<span class=\"str\">\"name_of_semaphore\"<\/span>, timeOutInMilliSec);\r\n    DoWork();\r\n\r\n}<span class=\"kwrd\">finally<\/span>\r\n{\r\n    objContainer.Ext().ReleaseSemaphore(<span class=\"str\">\"name_of_semaphore\"<\/span>);\r\n}<\/pre>\n<pre class=\"csharpcode\"><\/pre>\n<p>Often the name of the semaphore is derived from the object-id to ensure that the name is unique. A small example. Again we open two client object-containers. The first one grabs a lock. Then we check on another thread if we can get the lock there.<\/p>\n<pre class=\"csharpcode\"><span class=\"kwrd\">const<\/span> <span class=\"kwrd\">int<\/span> timeOut = 100;\r\nvar client1 = server.OpenClient();\r\nvar client2 = server.OpenClient();\r\n<span class=\"kwrd\">if<\/span> (!client1.Ext().SetSemaphore(<span class=\"str\">\"lock-demo\"<\/span>, timeOut))\r\n{\r\n    <span class=\"kwrd\">throw<\/span> <span class=\"kwrd\">new<\/span> InvalidOperationException(<span class=\"str\">\"Expect that I can get lock\"<\/span>);\r\n}\r\nvar couldGetLockOnClient1 = <span class=\"kwrd\">false<\/span>;\r\nvar couldGetLockOnClient2 = <span class=\"kwrd\">false<\/span>;\r\nvar thread = <span class=\"kwrd\">new<\/span> Thread(()=&gt;\r\n   {\r\n       couldGetLockOnClient1 = client1.Ext().SetSemaphore(<span class=\"str\">\"lock-demo\"<\/span>, timeOut);\r\n       couldGetLockOnClient2 = client2.Ext().SetSemaphore(<span class=\"str\">\"lock-demo\"<\/span>, timeOut);\r\n   });\r\nthread.Start();\r\nthread.Join();\r\nAssertTrue(couldGetLockOnClient1, <span class=\"str\">\"client1 can get lock. Also in other threads\"<\/span>);\r\nAssertTrue(!couldGetLockOnClient2, <span class=\"str\">\"client2 cannot get lock, since it's hold by client1\"<\/span>);<\/pre>\n<pre class=\"csharpcode\"><\/pre>\n<p>Here\u2019s one important thing to notice. The Thread we run can acquire the lock from the first client, but not from the second. So db4o locks work on a object-container. They have nothing to do with thread-synchronization like the lock-statement.<\/p>\n<p>Normally you build some kind of wrappers around this low-level-mechanism. For example to have this convenient way to lock objects.<\/p>\n<pre class=\"csharpcode\">dataBase.WithLock(yourAccount, myAccount)\r\n    .Execute(() =&gt;\r\n                 {\r\n                     var transferAmount = 100;\r\n                     yourAccount.Money -= transferAmount;\r\n                     myAccount.Money += transferAmount;\r\n                 });<\/pre>\n<p>This higher level locking-method is also responsible for ordering the locks, to avoid dead-locks. I\u2019ve added a simple implementation to this blog-entry. No guaranties that is correct though.<\/p>\n<p><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-locking.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-786\" title=\"db4o-locking\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-locking.png\" alt=\"db4o-locking\" width=\"600\" height=\"447\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-locking.png 600w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/db4o-locking-300x223.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<h3>More Concurrency Stuff<\/h3>\n<p>Well I think that I\u2019ve covered the very basic concurrency controls in db4o. There are some more building-blocks. For example you can send <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/client-server\/messaging.html\">messages around<\/a>. So you could notify clients about changes via the database-server. Furthermore db4o can keep version-numbers of object around. Maybe useful for for some kind of <a href=\"http:\/\/developer.db4o.com\/Documentation\/Reference\/db4o-7.12\/net2\/reference\/html\/reference\/client-server\/concurrency_control\/optimistic_locking.html\">optimistic locking<\/a>.<\/p>\n<div id=\"attachment_787\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/gone-mad.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-787\" class=\"size-medium wp-image-787\" title=\"gone-mad\" src=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/gone-mad-300x117.png\" alt=\"concurrency mad\" width=\"300\" height=\"117\" srcset=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/gone-mad-300x117.png 300w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/gone-mad-1024x399.png 1024w, https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/gone-mad.png 1200w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-787\" class=\"wp-caption-text\">concurrency mad<\/p><\/div>\n<h3>Next Time<\/h3>\n<p>Well I think I\u2019ve now covered the very basics of db4o. For more information read the <a href=\"http:\/\/developer.db4o.com\/Documentation.aspx\">documentation<\/a>, or ask it in the <a href=\"http:\/\/developer.db4o.com\/forums\/default.aspx\">forums<\/a>. I will certainly write about db4o sometime in the future =). But next time I\u2019ll take a brief look at another database-technology.<\/p>\n<p>Files:\u00a0<a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/Program1.cs\">DB4O-ClientServer.cs<\/a>, <a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/SimpleObject.cs\">SimpleObject.cs<\/a><\/p>\n<p>Lock-Utility: <a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/LockExtensions.cs\">LockExtensions.cs<\/a>, <a href=\"https:\/\/www.gamlor.info\/wordpress\/wp-content\/uploads\/2009\/11\/TestLockExtensions.cs\">TestLockExtensions.cs<\/a> (Unit-Test require <a href=\"http:\/\/www.nunit.org\/index.php\">NUnit <\/a>and <a href=\"http:\/\/code.google.com\/p\/moq\/\">Moq<\/a>)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So far we\u2019ve always used a single object container. This it the simplest way to used db4o. Just open an embedded database an use it. In this post I\u2019ll give&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[150],"tags":[21,100],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/posts\/779"}],"collection":[{"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/comments?post=779"}],"version-history":[{"count":8,"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/posts\/779\/revisions"}],"predecessor-version":[{"id":3826,"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/posts\/779\/revisions\/3826"}],"wp:attachment":[{"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/media?parent=779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/categories?post=779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gamlor.info\/wordpress\/wp-json\/wp\/v2\/tags?post=779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}