Skip to content

Commit 5d38595

Browse files
committed
Merge branch '3.8-dev'
2 parents df24318 + eff6a62 commit 5d38595

5 files changed

Lines changed: 272 additions & 2 deletions

File tree

CHANGELOG.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
158158
* Fixed bug in server `Settings` where it was referencing a property that was back in 3.3.0 and generating a warning log.
159159
* Improved performance of `Traversal.lock()` which was being called excessively.
160160
* Added log entry in `WsAndHttpChannelizerHandler` to catch general errors that escape the handlers.
161+
* Improved invalid plugin error message in Gremlin Console.
161162
* Added a `MessageSizeEstimator` implementation to cover `Frame` allowing Gremlin Server to better estimate message sizes for the direct buffer.
162163
* Fixed bug in Gremlin Console for field accessor issue with JDK17.
163164
* Improved logging around triggers of the `writeBufferHighWaterMark` so that they occur more than once but do not excessively fill the logs.
@@ -186,6 +187,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
186187
* Support hot reloading of SSL certificates.
187188
* Increase default `max_content_length`/`max_msg_size` in `gremlin-python` from 4MB to 10MB.
188189
* Added the `PopContaining` interface designed to get label and `Pop` combinations held in a `PopInstruction` object.
190+
* Fixed bug preventing a vertex from being dropped and then re-added in the same `TinkerTransaction`
189191
190192
[[release-3-7-3]]
191193
=== TinkerPop 3.7.3 (October 23, 2024)

gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ class Console {
150150
if (!io.quiet)
151151
io.out.println(Colorizer.render(Preferences.infoColor, "plugin activated: " + pluggedIn.getPlugin().getName()))
152152
} else if (!io.quiet) {
153-
io.out.println(Colorizer.render(Preferences.infoColor, "invalid plugin: " + pluginName))
153+
io.out.println(Colorizer.render(Preferences.infoColor, "invalid plugin: " + pluginName + " -- removing reference to this plugin."))
154+
154155
}
155156
}
156157

tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerElementContainer.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,20 @@ public void markDeleted(final TinkerTransaction tx) {
153153
}
154154
}
155155

156+
/**
157+
* Unmark element as deleted in the current transaction. Does nothing if the element was not previously marked as deleted.
158+
*
159+
* @see #markDeleted(TinkerTransaction)
160+
*/
161+
public void unmarkDeleted(final TinkerTransaction tx) {
162+
// only unmark as deleted if it was previously marked as deleted
163+
if (isDeletedInTx.get()) {
164+
usesInTransactions.decrementAndGet();
165+
isDeletedInTx.set(false);
166+
tx.markChanged(this);
167+
}
168+
}
169+
156170
/**
157171
* Mark element as changed in the current transaction.
158172
* A copy of the element is made and set as a value in the transaction.

tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraph.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,19 @@ public Vertex addVertex(final Object... keyValues) {
152152
if (container != null && container.get() != null)
153153
throw Exceptions.vertexWithIdAlreadyExists(idValue);
154154

155+
long version = txNumber;
156+
if (container != null && container.isDeleted() && container.getModified() != null) {
157+
// vertex being added was previously deleted
158+
// we need to reference the version from the deleted state when adding the vertex back
159+
version = container.getModified().version();
160+
container.unmarkDeleted((TinkerTransaction) tx());
161+
}
162+
155163
// no existing container, let's use new one
156164
if (container == null)
157165
container = newContainer;
158166

159-
final TinkerVertex vertex = new TinkerVertex(idValue, label, this, txNumber);
167+
final TinkerVertex vertex = new TinkerVertex(idValue, label, this, version);
160168
ElementHelper.attachProperties(vertex, VertexProperty.Cardinality.list, keyValues);
161169
container.setDraft(vertex, (TinkerTransaction) tx());
162170

tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraphTest.java

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
*/
1919
package org.apache.tinkerpop.gremlin.tinkergraph.structure;
2020

21+
import java.util.List;
2122
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
2223
import org.apache.tinkerpop.gremlin.structure.Edge;
2324
import org.apache.tinkerpop.gremlin.structure.T;
2425
import org.apache.tinkerpop.gremlin.structure.Vertex;
26+
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
2527
import org.apache.tinkerpop.gremlin.structure.util.TransactionException;
2628
import org.junit.Test;
2729

@@ -33,13 +35,15 @@
3335

3436
import static org.junit.Assert.assertEquals;
3537
import static org.junit.Assert.assertFalse;
38+
import static org.junit.Assert.assertNotEquals;
3639
import static org.junit.Assert.assertNotNull;
3740
import static org.junit.Assert.assertNull;
3841
import static org.junit.Assert.assertTrue;
3942
import static org.junit.Assert.fail;
4043

4144
public class TinkerTransactionGraphTest {
4245

46+
private static final String PROPERTY_NAME = "name";
4347
final Object vid = 100;
4448

4549
///// vertex tests
@@ -1503,4 +1507,245 @@ public void shouldHandleCorrectlyHandleCountForChangedAndReadElement() {
15031507

15041508
assertFalse(vertex.inUse());
15051509
}
1510+
1511+
@Test
1512+
public void shouldAllowDropAddVertexInSameTransaction() {
1513+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1514+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1515+
1516+
// create initial vertex with a property
1517+
final GraphTraversalSource gtx = tx.begin();
1518+
Vertex vertex = gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "test").next();
1519+
// add self-referencing edge
1520+
gtx.addE("tests").from(vertex).to(vertex).next();
1521+
gtx.tx().commit();
1522+
1523+
verifyCommittedSingleVertex(g, "test");
1524+
assertEquals(1, g.getEdgesCount());
1525+
final long originalVersion = g.getVertices().get(vid).getUnmodified().version();
1526+
1527+
// drop the vertex and re-create it without any properties or edges in the same transaction
1528+
final GraphTraversalSource gtx2 = tx.begin();
1529+
gtx2.V().drop().iterate();
1530+
gtx2.addV().property(T.id, vid).next();
1531+
gtx2.tx().commit();
1532+
1533+
verifyCommittedSingleVertex(g);
1534+
final TinkerVertex updatedVertex = g.getVertices().get(vid).getUnmodified();
1535+
assertEquals(0, g.getEdgesCount());
1536+
// version should have been updated
1537+
assertNotEquals(originalVersion, updatedVertex.version());
1538+
}
1539+
1540+
@Test
1541+
public void shouldAllowAddDropAddVertexInSameTransaction() {
1542+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1543+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1544+
1545+
final GraphTraversalSource gtx = tx.begin();
1546+
// create vertex with a property
1547+
final Vertex vertex = gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "test").next();
1548+
// add self-referencing edge
1549+
gtx.addE("tests").from(vertex).to(vertex).next();
1550+
// drop vertex
1551+
gtx.V(vid).drop().iterate();
1552+
// add vertex again with different properties but no edges
1553+
gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "updated name").next();
1554+
gtx.tx().commit();
1555+
1556+
verifyCommittedSingleVertex(g, "updated name");
1557+
assertEquals(0, g.getEdgesCount());
1558+
}
1559+
1560+
@Test
1561+
public void shouldAllowAddDropAddDropVertexInSameTransaction() {
1562+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1563+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1564+
1565+
final GraphTraversalSource gtx = tx.begin();
1566+
// create vertex with a property
1567+
final Vertex vertex = gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "test").next();
1568+
// add self-referencing edge
1569+
gtx.addE("tests").from(vertex).to(vertex).next();
1570+
// drop vertex
1571+
gtx.V(vid).drop().iterate();
1572+
// add vertex again with different properties but no edges
1573+
gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "updated name").next();
1574+
// drop the vertex again
1575+
gtx.V(vid).drop().iterate();
1576+
gtx.tx().commit();
1577+
1578+
assertEquals(0, g.getVerticesCount());
1579+
assertEquals(0, g.getEdgesCount());
1580+
}
1581+
1582+
@Test
1583+
public void shouldAllowAddDropAddDropVertexInMultipleTransactions() {
1584+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1585+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1586+
1587+
// first transaction
1588+
final GraphTraversalSource gtx1 = tx.begin();
1589+
// create vertex with a property
1590+
final Vertex vertex = gtx1.addV().property(T.id, vid).property(PROPERTY_NAME, "test").next();
1591+
// add self-referencing edge
1592+
gtx1.addE("tests").from(vertex).to(vertex).next();
1593+
// drop vertex
1594+
gtx1.V(vid).drop().iterate();
1595+
gtx1.tx().commit();
1596+
1597+
assertEquals(0, g.getVerticesCount());
1598+
assertEquals(0, g.getEdgesCount());
1599+
1600+
// second transaction
1601+
final GraphTraversalSource gtx2 = tx.begin();
1602+
// add vertex again with different properties but no edges
1603+
gtx2.addV().property(T.id, vid).property(PROPERTY_NAME, "updated name").next();
1604+
// drop the vertex again
1605+
gtx2.V(vid).drop().iterate();
1606+
gtx2.tx().commit();
1607+
1608+
assertEquals(0, g.getVerticesCount());
1609+
assertEquals(0, g.getEdgesCount());
1610+
}
1611+
1612+
@Test
1613+
public void shouldAllowDropAddUniqueVertexInConcurrentTransactions() {
1614+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1615+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1616+
1617+
// create first vertex and commit
1618+
final GraphTraversalSource gtx = tx.begin();
1619+
gtx.addV().property(T.id, vid).next();
1620+
gtx.tx().commit();
1621+
verifyCommittedSingleVertex(g);
1622+
1623+
// drop and add but don't commit
1624+
gtx.V(vid).drop().iterate();
1625+
gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "main thread").next();
1626+
1627+
// in another thread add, drop, add a different vertex and commit
1628+
int vid2 = (int) vid + 1;
1629+
runInNewThread(() -> {
1630+
final GraphTraversalSource gtx2 = g.tx().begin();
1631+
gtx2.addV().property(T.id, vid2).next();
1632+
gtx2.V(vid2).drop().iterate();
1633+
gtx2.addV().property(T.id, vid2).property(PROPERTY_NAME, "another thread").next();
1634+
gtx2.tx().commit();
1635+
});
1636+
1637+
// commit the drop add
1638+
gtx.tx().commit();
1639+
// both vertices should exist
1640+
assertEquals(2, g.getVerticesCount());
1641+
assertEquals("main thread", g.getVertices().get(vid).getUnmodified().properties.get(PROPERTY_NAME).get(0).value());
1642+
assertEquals("another thread", g.getVertices().get(vid2).getUnmodified().properties.get(PROPERTY_NAME).get(0).value());
1643+
}
1644+
1645+
@Test
1646+
public void shouldPreventDropAddVertexInConcurrentTransactions() {
1647+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1648+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1649+
1650+
// create initial vertex and commit transaction
1651+
final GraphTraversalSource gtx = tx.begin();
1652+
gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "original").next();
1653+
gtx.tx().commit();
1654+
verifyCommittedSingleVertex(g, "original");
1655+
1656+
// drop the vertex and add it back but don't commit
1657+
gtx.V(vid).drop().iterate();
1658+
gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "should fail!").next();
1659+
1660+
// drop the vertex and add it back in another thread and commit
1661+
runInNewThread(() -> {
1662+
final GraphTraversalSource gtx2 = g.tx().begin();
1663+
gtx2.V(vid).drop().iterate();
1664+
gtx2.addV().property(T.id, vid).property(PROPERTY_NAME, "updated").next();
1665+
gtx2.tx().commit();
1666+
});
1667+
1668+
// try to commit the drop and add in main thread - should fail
1669+
try {
1670+
gtx.tx().commit();
1671+
fail("should throw TransactionException");
1672+
} catch (TransactionException e) {
1673+
assertEquals("Conflict: element modified in another transaction", e.getMessage());
1674+
verifyCommittedSingleVertex(g, "updated");
1675+
}
1676+
}
1677+
1678+
@Test
1679+
public void shouldPreventDropUpdateVertexInConcurrentTransactions() {
1680+
final TinkerTransactionGraph g = TinkerTransactionGraph.open();
1681+
final TinkerTransaction tx = (TinkerTransaction) g.tx();
1682+
1683+
// create initial vertex and commit transaction
1684+
final GraphTraversalSource gtx = tx.begin();
1685+
gtx.addV().property(T.id, vid).property(PROPERTY_NAME, "original").next();
1686+
gtx.tx().commit();
1687+
verifyCommittedSingleVertex(g, "original");
1688+
1689+
// drop but don't commit
1690+
gtx.V(vid).drop().iterate();
1691+
1692+
// modify the vertex in another thread and commit
1693+
runInNewThread(() -> {
1694+
final GraphTraversalSource gtx2 = g.tx().begin();
1695+
gtx2.V(vid).property(PROPERTY_NAME, "updated").next();
1696+
gtx2.tx().commit();
1697+
});
1698+
1699+
// try to commit the drop vertex - should fail
1700+
try {
1701+
gtx.tx().commit();
1702+
fail("should throw TransactionException");
1703+
} catch (TransactionException e) {
1704+
assertEquals("Conflict: element modified in another transaction", e.getMessage());
1705+
verifyCommittedSingleVertex(g, "updated");
1706+
}
1707+
}
1708+
1709+
private void runInNewThread(final Runnable runnable) {
1710+
final Thread thread = new Thread(runnable);
1711+
thread.start();
1712+
try {
1713+
thread.join();
1714+
} catch (InterruptedException e) {
1715+
throw new RuntimeException(e);
1716+
}
1717+
}
1718+
1719+
private void verifyCommittedSingleVertex(final TinkerTransactionGraph g) {
1720+
verifyCommittedSingleVertex(g, null);
1721+
}
1722+
1723+
private void verifyCommittedSingleVertex(final TinkerTransactionGraph g, final String namePropertyValue) {
1724+
// graph should only have a single vertex
1725+
assertEquals(1, g.getVertices().size());
1726+
1727+
// the single vertex should have the expected id
1728+
final TinkerElementContainer<TinkerVertex> vertexContainer = g.getVertices().get(vid);
1729+
1730+
// container should be committed and not have an update in progress
1731+
assertFalse(vertexContainer.isChanged());
1732+
assertFalse(vertexContainer.inUse());
1733+
1734+
// the element in the container should have the same id
1735+
// there are multiple ways to obtain the container element's id and they should be consistent
1736+
assertEquals(vid, vertexContainer.getElementId());
1737+
assertEquals(vid, vertexContainer.get().id());
1738+
final TinkerVertex unmodified = vertexContainer.getUnmodified();
1739+
assertEquals(vid, unmodified.id());
1740+
1741+
if (namePropertyValue != null) {
1742+
final Map<String, List<VertexProperty>> properties = g.getVertices().get(vid).getUnmodified().properties;
1743+
assertEquals(1, properties.size());
1744+
final List<VertexProperty> nameProperties = properties.get(PROPERTY_NAME);
1745+
assertEquals(1, nameProperties.size());
1746+
assertEquals(namePropertyValue, nameProperties.get(0).value());
1747+
} else {
1748+
assertNull(vertexContainer.getUnmodified().properties);
1749+
}
1750+
}
15061751
}

0 commit comments

Comments
 (0)