\(\textsf{TVA}\): A Version-aware Temporal Graph Storage System for Real-time Analytics


Abstract

Analyzing temporal graphs can reveal valuable insights that are typically hidden in static graphs. Unfortunately, existing graph storage systems either lack native temporal support or suffer from high latency when querying temporal graphs. This paper presents \(\textsf{TVA}\), a new temporal graph storage system designed for efficient temporal query processing. First, \(\textsf{TVA}\) introduces a specialized multi-version storage architecture that separates version metadata from actual data, i.e., the property values associated with different versions of vertices and edges. This architecture enables efficient version retrieval for a vertex or edge by quickly locating valid version metadata and directly dereferencing it to access the corresponding property values. Second, we design tailored data structures, namely the temporal table and enhanced hopscotch-based hashing, to compactly organize the version metadata of adjacent vertices and edges, thus reducing random I/O for metadata lookups during the neighborhood scan initiated from a vertex. Finally, to further accelerate neighborhood scans over multiple vertices, we propose a version-skipping strategy that reuses temporal information obtained from prior scans, thereby avoiding redundant metadata lookups across scans. Empirical evaluations demonstrate that \(\textsf{TVA}\) achieves up to \(9.9\times\) lower temporal query latency and \(2.2\times\) lower storage overhead compared to state-of-the-art temporal graph storage systems.

PVLDB Reference Format:
. . PVLDB, 19(10): 2536 - 2548, 2026.
doi:10.14778/3828612.3828613

1

1 Introduction↩︎

A temporal graph, also known as a dynamic or time-varying graph, represents the evolution of relationships between entities, the entities themselves, or both over time [1], [2]. Unlike static graphs, temporal graphs retain rich temporal information, which is crucial for a wide range of applications, such as fraud detection [3][5] and social network analysis [6][8].

Figure 1: An Example of Financial Temporal Graph.

Example 1. Figure 1 shows a typical temporal graph in the financial domain, where vertices represent entities, such as users, credit cards, and bank accounts, and edges denote their relationships, such as ownership and transaction events. Each vertex and edge can have multiple versions over time. Suppose that user Bob’s phone IP address, stable in New York throughout the time interval [\(t_0\), \(t_{n}\)), suddenly changes to Singapore at time \(t_n\). This IP change creates a new version of the vertex representing Bob’s phone. Shortly afterward, at time \(t_{n+1}\), Bob initiates a transaction in Singapore. The pattern of a risky transaction following a sudden location change strongly suggests that Bob’s account has been compromised. To detect such risks, one can leverage a fraud detection model with relevant graph data [9][11]. However, if only a static graph (e.g., at time \(t_{n+1}\)) is provided, the model may fail to identify the fraud due to the absence of recent geolocation change information. Therefore, it is essential to obtain a temporal graph within an appropriate time period, e.g., [\(t_{n-1}\), \(t_{n+1}\)], that includes the necessary temporal context for effective fraud detection. 0◻

Continuing from Example 1, a typical temporal query retrieves transactions that occur between [\(t_{n-1}\), \(t_{n+1}\)], along with their corresponding user versions. However, some graph systems [12][14] retain only the current state of the graph, making them unable to handle such queries. In contrast, systems like T-GQL [15] store versions as separate objects, incurring significant storage overhead. Snapshot-based approaches, such as Clock-G [16], periodically store full snapshots of the graph and maintain logs of deltas between snapshots. It requires expensive reconstruction of snapshots from deltas. A recent system, AeonG [17], applies fine-grained versioning but incurs redundant traversal and extensive random I/O when scanning version chains across large subgraphs. Furthermore, general-purpose storage systems are also ill-suited for temporal graphs: RDBMSs suffer from computationally expensive join operations during multi-hop traversals, while Key-Value (KV) stores lack structural awareness, leading to inefficient temporal range scans.

Addressing these challenges requires a new storage architecture that can efficiently locate relevant versions of a given time condition. Our goal is to minimize the version traversal cost during temporal query processing, thereby improving the overall performance. However, achieving this requires addressing three key challenges: First, each vertex or edge may contain multiple properties, and updates typically affect only a subset of them. To reduce storage overhead, systems often adopt delta-based encoding [16], [17], where only the modified properties are stored in each version. However, this makes it infeasible to reconstruct a complete version without traversing its version chain. As a result, achieving fast version retrieval with minimal storage redundancy is not straightforward. Second, a vertex can be connected to multiple edges, each maintaining its own version chain. Existing data structures are primarily designed and optimized for accessing the latest version of data, while historical versions are often appended to each data item using a simple linked structure [15][17]. This design prevents historical data queries from benefiting from the core optimization mechanisms of the system. Designing a unified structure to co-manage these version chains efficiently, particularly for high-degree vertices, remains a significant challenge. Third, temporal queries typically involve scanning the neighborhoods of multiple vertices [8], [18]. However, existing systems process each of these neighborhood scans independently. Leveraging this cross-query temporal locality to avoid redundant version traversals and computation is key to enhancing overall query efficiency.

In this paper, we present \(\textsf{TVA}\), a version-aware graph storage system designed for efficient temporal query processing. We introduce a multi-version storage architecture that separates version metadata from actual data. The actual property values of graph objects are stored in a compact structure, with pointers in the metadata, allowing retrieval by first locating the metadata and then dereferencing it. This minimizes random I/O and enables low-latency version retrieval (Challenge ). We also design specialized data structures: a temporal table for compact vertex metadata and a hopscotch-based hashing structure for topological relationships. We use a hopscotch-inspired algorithm to co-locate temporally related metadata in the same hash bucket, storing versions in temporal order with a bounded offset. This allows logarithmic lookup time for version metadata, while maintaining access locality by dynamically relocating hot and cold data based on update frequency. SIMD techniques further accelerate version evaluation (Challenge ). Finally, we propose a version-skipping approach that links versions within a snapshot, enabling subsequent scans to resume from previously retrieved metadata, avoiding redundant lookups (Challenge ).

In summary, we make the following contributions:

  • We propose \(\textsf{TVA}\), a new temporal graph storage system designed for efficient temporal query processing.

  • We design a multi-version storage architecture tailored for temporal graphs, which reduces redundant version traversal cost without sacrificing storage efficiency.

  • We introduce a hopscotch-based hash structure to enable bounded lookup time when performing neighborhood scans under given time conditions. In addition, we utilize SIMD technology to enable parallel query processing.

  • We propose a version-skipping approach that reuses temporal information from previous scans to improve temporal query performance.

  • We conduct extensive experimental evaluations on widely-used benchmarks, demonstrating that \(\textsf{TVA}\) achieves up to \(2.2\times\) lower storage overhead and up to \(9.9\times\) faster temporal query processing compared to state-of-the-art graph systems.

2 Background↩︎

In this section, we review existing temporal graph storage systems, discuss the basic techniques used in the design of \(\textsf{TVA}\), and formally define the problem that \(\textsf{TVA}\) aims to address.

2.1 Temporal Graph Model↩︎

Temporal Graph Definition. A temporal graph is defined as \(\mathcal{G} = (\mathcal{X}, \mathcal{E})\):

  • Each vertex \(x \in \mathcal{X}\) is associated with a set of properties \(\rho\) and an active time interval \(\tau = [t_{\text{start}}, t_{\text{end}})\).

  • Each edge \(e \in \mathcal{E}\) is a tuple \((src, dst, \tau, \rho)\), where \(src, dst \in \mathcal{X}\) are the endpoints of the edge, \(\tau = [t_{\text{start}}, t_{\text{end}})\) denotes the edge’s active time interval, and \(\rho\) is a set of properties.

Each vertex and edge may possess multiple labels. To simplify the subsequent discussion, we assume that each vertex or edge has a single label. For example, we denote a user vertex as \(x^{\text{user}}\), a phone vertex as \(x^{\text{phone}}\), and a transaction edge as \(e^{\text{txn}}\). A one-to-one pair (e.g., user-phone) is denoted as \((x^{\text{user}}, x^{\text{phone}})\). We denote the different versions of a vertex (edge) as \(x_n.v_m\) (\(e_n.v_m\)), where \(x_n\) (or \(e_n\)) refers to the \(n\)-th vertex (or edge), and \(v_m\) indicates its \(m\)-th version. Throughout this paper, both vertices and edges are collectively referred to as graph objects. For versioning semantics, TVA supports both logical version numbers (e.g., transaction IDs) and physical timestamps. Without loss of generality, we use physical time intervals \([t_{start}, t_{end})\) in our examples.

Temporal Graph Operation. Temporal graph updates evolve the state of the graph while preserving historical information. The supported operations are as follows:

  • Create Vertex or Edge: Add a graph object \(o\) to the temporal graph \(\mathcal{G}\) and assign corresponding label and properties, with a time version assigned as \(\tau = [t_1, +\infty)\).

  • Delete Vertex or Edge: Find the graph object \(o \in \mathcal{X} \cup \mathcal{E}\) of the corresponding version, update its \(\tau = [t_1,+\infty)\) to \([t_1, t_2)\) to mark its deletion.

  • Update Vertex or Edge: Find the graph object \(o \in \mathcal{X} \cup \mathcal{E}\) of the corresponding version, update its \(\tau = [t_1, +\infty)\) to \([t_1, t_2)\) and mark it as a historical version. Then, create a new version with \(\tau = [t_2, +\infty)\) to represent the latest version and apply the necessary updates to the vertex or edge.

Temporal Graph Query. Temporal graph queries aim to retrieve graph objects that satisfy both a temporal condition and a predicate over their attributes. Given a time point or interval \(t_q\), a temporal query returns all vertices and edges whose validity intervals intersect \(t_q\). The result can be viewed as a static graph snapshot obtained by filtering out objects that are not valid during \(t_q\). Formally, let a graph object \(o \in \mathcal{X} \cup \mathcal{E}\) have a unique identifier \(o.id\) and a validity interval \(o.\tau\). A temporal query is defined as: \[\label{func:find95all} O_{\text{query}} = \left\{o \in \mathcal{X} \cup \mathcal{E} \;\middle|\; P(o) = \text{true} \land\; o.\tau \cap t_q \neq \varnothing \right\},\tag{1}\] where \(P(o)\) is a predicate over graph objects. This formulation generalizes common temporal query types. For example, retrieving a specific object corresponds to \(P(o): o.id = id_q\); and retrieving a set of objects corresponds to a general predicate, e.g., \(o.\rho[\text{Location}] = \text{``New York''}\).

2.2 sec:Hopscotch32Hash↩︎

Figure 2: An Example of Hopscotch Hash Table.

Hopscotch Hash [19] is an open-addressing hash algorithm that constrains each key to a fixed-size neighborhood (\(\mathcal{H}\)) near its primary slot. Each slot maintains an \(\mathcal{H}\)-bit bitmap, indicating which of the next \(\mathcal{H}\) slots hold its key. During insertion, if a key’s primary slot is occupied, the algorithm identifies the first available empty slot. If this empty slot is outside the desired neighborhood, the algorithm searches the previous \(\mathcal{H}\)-1 items to find an item that can be swapped into the empty slot without violating its neighborhood constraint. The swap is repeated until an empty slot is moved into the neighborhood of the key’s primary slot. Figure 2 shows an example of this process. In the left subfigure, keys A, B, and C share the same hash value and are inserted into slots within the hop range of A’s primary slot (Bitmap=1110). Similarly, D is placed in its own primary slot (Bitmap=1000). When inserting E, which has the same hash value as D, its primary slot is full. Through the hopscotch insertion process, E is moved into a slot (Bitmap=1001) that still falls within the hop range of D’s primary slot.

Although the original hopscotch insertion algorithm is effective in resolving hash collisions, it fails to preserve data ordering and is not designed for managing temporal data versions. Instead of a direct application, we innovatively extend and redesign the hopping mechanism to propose an enhanced hopscotch-hash algorithm, which is specially tailored for organizing multiple temporal versions of data objects within a unified hash table. Our design addresses the specific challenges of temporal locality and version lookup efficiency, as detailed in § 4.2.

Figure 3: Overall Architecture of \textsf{TVA}.

3 System Overview↩︎

\(\textsf{TVA}\) supports efficient temporal graph management through two key components: the storage engine and the query engine.

3.1 Storage Engine↩︎

As shown in Figure 3, \(\textsf{TVA}\)’s storage engine adopts a hybrid architecture with two specialized components, designed to handle the different types of data in a temporal graph. An incoming update is routed by the Relationship Classifier to the Temporal Property Storage if it modifies vertices’ intrinsic properties, or to the Dynamic Topology Storage if it creates, deletes, or modifies an edge between vertices. The central idea behind these two components is to separately manage version metadata and the actual data.

Temporal Property Storage: This component is optimized for efficient management of vertex properties (e.g., a user’s name or a phone’s IP address). At any moment, each vertex maintains at most one current value per property; therefore, a columnar storage layout is employed to facilitate efficient access to current data [20]. Historical values for each property are stored separately in a specialized structure called Temporal Buffer. Versioning metadata is maintained by Temporal Table structure. Upon property updates, the existing value is not overwritten; instead, it is transferred to the corresponding Temporal Buffer, and a new record is added to the Temporal Table to index this update. We will provide further details in § 4.1.

Dynamic Topology Storage: This component is responsible for managing the dynamic connections between vertices (e.g., transactions or social links), where a vertex can have multiple edges for the same relationship. Such dynamism makes columnar layouts for current data, where data location can be calculated by a predictable offset [21], unsuitable. We observe that for the edges of the same vertex, there is no strong order correlation among different edges, but for different versions of the same edge, it is best to store them in chronological order to speed up lookup. To achieve this, we construct a storage structure based on a hopscotch algorithm. This structure stores all edges of a vertex within a single hash bucket, and it organizes all versions of an edge chronologically within a “neighborhood”. In addition, we optimize dynamic topology storage through a cold-hot data separation strategy. We will introduce the details in § 4.2.

3.2 Query Engine↩︎

The query engine processes user queries and retrieves relevant graph data from the hybrid storage. \(\textsf{TVA}\) supports all temporal graph operations described in § 2.1.

For Temporal Property Storage, all basic metadata for each vertex is stored in the Header of the Temporal Table. \(\textsf{TVA}\) first identifies the target vertex, then locates its Header in the Temporal Table based on the offset to check whether the data exists in the Current Storage. If present, the data can be read directly from the corresponding column; if not, \(\textsf{TVA}\) performs a binary search to locate the correct Version in the Temporal Table, using the offset recorded in that Version to determine the data position. Additionally, when a query retrieves all data from the same timestamp, NextOffset structure is introduced to use information from the previous row, thereby accelerating queries for subsequent rows. Further details are provided in § 5.1.

For Dynamic Topology Storage, we similarly start by identifying the relevant vertex. Subsequently, using the HashFunction and LatestValuePtr in the storage structure pointed to by that vertex, we determine the potential range for the edge we need to find. Finally, we locate the specific data within this range. Further details are provided in § 5.2.

These two structures enable efficient temporal queries on both properties and topology. In addition, these compact storage layouts enable us to efficiently leverage SIMD hardware acceleration, thereby further speeding up temporal queries. More details are provided in § 5.3.

Example 2. Figure 3 presents example query statements that demonstrate operations on both storages. The query highlighted in purple corresponds to Temporal Property Storage, while the one in orange illustrates an operation on Dynamic Topology Storage.

The purple query corresponds to: “Snapshot of IP addresses at \(t_{n+1}\).” It locates the first vertex \(x_1\) and performs a binary search on the Temporal Table to find the matching version. For subsequent queries, the previously identified Version can be leveraged to quickly locate potential neighboring position of the next row’s Version, thereby narrowing the search. The orange query resolves the question, “What are the transaction amounts at time \(t_{n+1}\)?” We take Bob’s transaction as an example. Let Bob’s vertex be \(x_3\), and the relevant edge be \(e_1\). Using the HashFunction, we can locate the oldest version of \(e_1\), while LatestValuePtr points to its latest version. Searching within this range then retrieves the desired data. If we need to search for the next edge of \(x_3\), we can similarly utilize the information obtained in \(e_1\). 0◻

4 Storage Design↩︎

In this section, we detail the design of the Temporal Property Storage and dynamic topology storage. We provide detailed pseudocode for Create, Read, Update, and Delete (CRUD) operations to explicitly demonstrate how TVA handles data manipulation.

4.1 Temporal Property Storage↩︎

Key Idea. We propose a multi-version storage architecture for vertex properties, based on the observation that each vertex maintains at most one current value for any given property. The current state is stored in a columnar layout, which allows efficient direct access for current queries. To address the inefficiency of traditional methods that require traversing version chains for historical queries, we introduce a dedicated historical storage design. The core of this design is the Temporal Table, which separates version metadata from property values and stores all historical data in a unified, compact region. Metadata entries are stored in a tightly packed, contiguous format to enable rapid direct lookups. As a result, retrieving any version requires only locating its metadata and dereferencing the corresponding value, thereby minimizing overhead. In addition, we introduce NextOffset structure within the Temporal Table that links temporally-related versions across different vertices, accelerating snapshot queries that require data from the same timestamp.

Figure 4: The Storage Format for Vertex Properties.

Storage Format. As illustrated in Figure 4, the fundamental layout of Current Storage in this component is columnar storage. The vertex IDs allocated according to the chosen scheme serve as primary keys. Properties are organized into distinct columns and indexed by their labels and property keys. To manage temporal data and updates effectively, we introduce two critical extensions:

Temporal Table: For each vertex ID, Temporal Table manages version metadata and stores it separately from the corresponding property values. Each row consists of a Header and several Version entries. The Header contains a LOCK field for concurrency control during updates, as well as an ExistBitmap, which indicates which property columns currently have data for the corresponding vertex. Each Version entry represents an update to the vertex and records the historical versions of the relevant properties. Specifically, it includes a Lifecycle field that specifies the validity interval \([t_{\text{start}}, t_{\text{end}})\) of that historical state, and a ModifyBitmap that identifies which property columns are involved in this version. Additionally, an array of Offset\(_1\) to Offset\(_k\) is included, where \(k\) denotes the number of property columns. Each Offset\(_j\) points to the storage location of the historical value for the \(j\)-th property within the Temporal Buffer; if it is set to nullptr, it instead refers to the value in the Current Storage. Finally, the Version structure contains a NextOffset pointer, which links to a specific Version in the next row. When a new Version is created, this pointer is set to the latest Version in the following row at that time. These NextOffset pointers collectively form multiple version chains, through which we can efficiently retrieve subsequent data points within the same temporal context.

Historical Storage: This is a large, append-only region where the actual historical values are stored. Each property has its corresponding Temporal buffer. When a property is updated, its previous value is written contiguously into its buffer.

Figure 5: Create and Update Operations in Temporal Property Storage

Insert, Update or Delete. We design the system to maintain current data efficiently while archiving historical versions without costly reorganizations. The core idea is to physically separate current and historical states. In our system, a delete operation is treated as a special type of update operation and how read operations are performed will be elaborated in Section 5. To explicitly demonstrate how \(\textsf{TVA}\) handles data manipulation, we present the pseudocode for Graph operations in Algorithm 5.
For Create operations (lines 1-5), \(\textsf{TVA}\) directly writes the value to the Current Storage to ensure \(O(1)\) access for latest-state queries, while initializing the version metadata in the Temporal Table.
The Update process (lines 6-12) is the core of our version-aware design. When vertex properties are updated at time \(t\), we proceed as follows: First, to prioritize the performance of reading the latest graph state, the new property value (\(v_{\text{new}}\)) is written directly into the corresponding property column of the Current Storage (line 8), implicitly marking its validity as \([t, +\infty)\). Simultaneously, the previous property value (\(v_{\text{old}}\)) is preserved: it is appended to the append-only Historical Storage (Temporal Buffer), as shown in line 9. Finally, a new Version entry is created and appended to the Temporal Table to manage the metadata of this state change (lines 10-12). This new entry records the lifecycle end time (\(t\)) of the old version (representing the interval \([t_{\text{current}}, t)\)) and stores the offset pointing to the archived \(v_{\text{old}}\). Additionally, as detailed in Section 4.1, this new version entry maintains a NextOffset pointer, linking it to the latest version of the subsequent vertex to accelerate cross-vertex snapshot scanning.

4.2 Dynamic Topology Storage↩︎

Key Idea. For dynamic topology and its associated edge properties, it is difficult to use a simple vertex-to-storage mapping because one vertex can be linked to many data items of the same relationship. Additionally, the uncertainty in the number of edges associated with each vertex makes it difficult to predict the storage requirements needed for compact storage. This issue is even more obvious in real graphs, where some vertices have many edges and are updated often [22], [23], leading to a large number of old versions. To solve this, we use a mixed storage method: infrequently updated (“cold”) data is stored in a SparseArray, while frequently updated (“hot”) data is managed by an improved HopscotchHash Table that is designed for handling time-related data. This structure stores all objects with the same relationship in the same hash bucket. Our proposed “Hopscotch” algorithm guarantees that all historical versions of a given data item are stored in temporal order with a bounded offset.

Figure 6: The Storage Format for Dynamic Topologies.

Storage Format. The overall architecture is shown in Figure 6. The system stores data based on vertex degrees using two structures: SparseArray for “cold” data storage and HopscotchHash Table for “hot” data storage. We define concrete criteria for this division: A vertex \(v\) is classified as “hot” and migrated to the HopscotchHash Table if its degree exceeds a threshold \(T_{deg}\), or its total version count reaches \(T_{ver}\), formally expressed as \(\sum_{e \in E(v)} N_v(e) \ge T_{ver}\) (where \(N_v(e)\) is the version count of edge \(e\)). This migration is triggered asynchronously to minimize blocking. A background worker allocates a new hash bucket, re-organizes the existing data, and atomically updates the pointer, allowing the current write operation to complete without blocking. Conversely, migration from Hopscotch back to SparseArray is rare; it only occurs during a vacuum/purge operation where data prior to a specific timestamp is cleared, reducing the edge count below the threshold. A Vertex Map directs different vertices to their respective storage units. Both structures employ a unified minimal storage unit for edge information that encapsulates three components: VE Value \((e_i, x_j)\), which captures the graph’s topological information; Lifecycle, specifying the validity interval \([t_{\text{start}}, t_{\text{end}})\) of that historical state; and EPPtr, which points to the specific storage location of the edge’s corresponding property. Next, we will introduce these two specific data structures.

SparseArray: For “cold” data storage, it allocates fixed-size data slots for each vertex. This array-based structure offers good spatial locality, facilitating efficient scanning operations. Edges are inserted in a temporally ordered manner within a vertex’s allocated space, simplifying subsequent time-series queries for that vertex’s edges. When the number of elements reaches its maximum limit, all data will be transferred to the HopscotchHash Table for storage.

HopscotchHash Table: For “hot” data storage, which are associated with numerous and frequently updated edges, HopscotchHash Table is employed. This structure is designed for efficient insertion and \(O(1)\) retrieval of the latest edge data, with historical versions accessible with logarithmic time complexity. The architecture of the HopscotchHash Table integrates several components: Header, which maintains metadata such as the label, the MaxHopDistance parameter, and Lock for concurrency control; HashFunction that maps edges to 64-bit hash values, while additionally recording the upper seven bits in the HopInfo to enhance SIMD query operations, as detailed in § 5.3. The position indicated by HashFunction can be considered as the initial location of the corresponding item. To track the most recent version of each item, a dedicated hash table, LatestValuePtr, indexes data item identifiers to the storage location of their latest versions in the HashBucket.

Version control and efficient traversal are further enabled by four arrays collectively referred to as HopInfo. ControlInfo stores the upper seven bits of the hash value for each HashBucket slot. FrontInfo and BackInfo record bidirectional version offsets, respectively tracking links to the chronologically preceding and succeeding versions of a given entry. MaxHopDistance guarantees that all versions of a particular edge are stored within a bounded offset. In addition, the NextOffset array facilitates fast queries for subsequent data entries sharing the same timestamp.

Insert, Update, or Delete. These graph operations require finding the corresponding data insertion position in the Hopscotch Hash Table. Therefore, the key challenge is to resolve conflicts between temporal records. To address this issue, we propose a novel Hopscotch algorithm for temporal data storage.

Two Hopscotch Ways. When inserting a new version of a data item, we first obtain the most recent position \(p\) via LatestValuePtr. The new version must be inserted within the range \([p+1, p+\texttt{MaxHopDistance}]\). If no empty slot is found within this range, the system must create a free slot via a “Hopscotch” algorithm, which involves relocating existing data items.

Figure 7: Hop Update Process

In practice, to efficiently perform the operations illustrated in the example, we employ two distinct strategies based on the distance between the target location and the nearest empty slot.

As shown in Algorithm 7, we define a threshold \(d_{\text{judge}}\) (line 4). When the distance between the empty bucket and the target position is below this threshold, we employ the single-step hopping method (lines 5-10), where each item is checked to see if it can be relocated. However, when the empty bucket is too far from the target position, sequentially checking each item would incur high computational overhead. In this case, we adopt a second method, the chain hopping approach (lines 11-19). This method checks each data item to determine its oldest version location by the FrontInfo (lines 12-14), and if the position is after the target location, we can move the entire data item backward (lines 15-17). This action does not violate any hop distance constraints, so no further validation is necessary. The chain hopping operation involves moving each data item to the position of its next item, with the last item being moved to the empty bucket (lines 24-31). This approach quickly brings the data item closer to the target position, thus reducing the number of comparisons. The hop-bounded layout guarantees that the search scope for a specific version is restricted within a localized neighborhood in the hash bucket, preventing full-chain traversal even when version chains become long. This design preserves logarithmic retrieval time with respect to the number of versions per edge.

Discussion. Although our design is inspired by the Hopscotch hashing algorithm, it differs fundamentally in its objective. The original algorithm focuses on resolving hash collisions by using a bitmap to keep keys within a fixed neighborhood of their home bucket. In contrast, our approach is designed to support ordered storage and efficient retrieval of multiple temporal versions of a single data item. To this end, we replace the bitmap with item-centric FrontInfo and BackInfo, which explicitly capture inter-version relationships. This design enables two novel mechanisms, single-step hopping and chain hopping, where the latter leverages these inter-version links and is unique to our structure.

5 Temporal Query Engine↩︎

In this section, we present the query processes and time complexity analysis for Temporal Property Storage and Dynamic Topology Storage, and introduce the use of SIMD technology.

5.1 Query Engine for Vertex Properties↩︎

To access the latest property version, the system directly reads from the relevant column using the vertex ID offset, avoiding version management overhead. For temporal queries at time t, it consults the vertex’s Temporal Table and uses each Version’s ModifyBitmap and Lifecycle to locate the appropriate version entry via binary search. When multiple properties are queried simultaneously, parallel lookups are employed after the corresponding versions are identified to further accelerate retrieval. The actual data value’s location is then determined by the Offset: if null, the current value is used; otherwise, the value is retrieved from the Temporal Buffer. Further, to optimize the requirement of finding all objects at the same time point, the system finds the first object as described above. Then, for all subsequent objects, it uses the NextOffset pointer from the previously queried Version to quickly locate their relevant Version, reducing comparison overhead and avoiding repeated full searches.

Theorem 1. Let the NextOffset of \(\texttt{Version}_i\) point to the next row’s \(\texttt{Version}_j\), with the lifecycles of \(\texttt{Version}_i\) and \(\texttt{Version}_j\) given by \([ti_{start}, ti_{end})\) and \([tj_{start}, tj_{end})\), respectively. Then, the lifecycles satisfy: \[[ti_{start}, ti_{end}) \cap [tj_{start}, tj_{end}) \neq \varnothing \land ti_{start} > tj_{start}\]

Please see the proofs in our github [24]. This theorem allows us to start the version search for the next object from a temporally relevant position, effectively narrowing down the search space.

I/O Analysis. Here, we denote the number of vertices as \(N\), with an average version count per vertex represented by \(N_v\).

Read the latest version: Accessing the most current data is highly efficient in our system. It involves a direct lookup in the primary property columns, resulting in approximately one random access to fetch the data for the given object’s property.

Read a specific historical version: To retrieve a historical version, \(\textsf{TVA}\) first performs a random access to locate the entries in the Temporal Table, then uses binary search to find the correct Version. It finally retrieves the actual data from the Temporal Buffer using the Offset. This approach achieves a time complexity of \(O(\log{N_v})\), compared to \(O(N_v)\) for version chain methods. Moreover, since Version uses a contiguous array, \(\textsf{TVA}\) only incurs random I/O at the final step, whereas version chain approaches require random I/O at each search step.

Read specific historical versions for all objects: When querying a full graph snapshot at time \(t\), locating the first vertex’s version is similar to a single-point query. For subsequent vertices, \(\textsf{TVA}\) uses the NextOffset pointer to jump directly to the relevant region for each vertex, avoiding a full search. Finding the exact version within this region typically requires a small number \(k\) of probes, leveraging temporal locality. Thus, after the initial search, the complexity for the remaining \((N-1)\) vertices is approximately \(O((N-1)k)\), where \(k\) is a small constant, resulting in an overall complexity of \(O(N)\). In contrast, systems without this optimization may require \(O(N \cdot N_v)\) time for \(N\) independent full searches. Therefore, NextOffset substantially reduces the complexity of snapshot queries.

5.2 Query Engine for Dynamic Topology↩︎

For cold data, since each vertex data is stored in a fixed, compact array, traversing the array can quickly locate the required version. For hot data in the HopscotchHash Table, the query process is as follows: to access the latest version of an edge, the system uses LatestValuePtr to directly locate the storage position by destination vertex ID, achieving \(O(1)\) time complexity. For historical data at a specific time \(t\), the system combines HashFunction and LatestValuePtr to determine the bounds of available versions, with storage distances capped by MaxHopDistance. Leveraging SIMD instructions, up to 16 slots can be compared simultaneously; when \(\texttt{MaxHopDistance} \leq 16\), these versioned edges can be regarded as tightly stored, as validated in experiment 7.5.1. How we leverage SIMD operations to accelerate query processing will be detailed in § 5.3. Overall, this enables fast, binary-search-style queries in a compact region, reducing time complexity from linear to logarithmic compared to traditional chained traversals. Additionally, for querying all objects at the same time point, NextOffsetInfo can be used to accelerate subsequent lookups, following a process similar to that described in § 5.1.

Table 1: Time Complexity of Different Operations in .
Operation HopscotchHash Table Temporal Table
Situation Time Complexity Time Complexity
Read the latest version \(N_v \cdot N_e \leq S\) \(O(S)\) \(O(1)\)
\(N_v \cdot N_e > S\) \(O(1)\)
Read a specific historical \(N_v \cdot N_e \leq S\) \(O(S)\) \(O(\log(N_v))\)
Version \(N_v \cdot N_e > S\) \(O(\log(N_v))\)
Read specific historical \(N_v \cdot N_e \leq S\) \(O(S)\) \(O(N)\)
versions for all objects \(N_v \cdot N_e > S\) \(O(N \cdot N_e)\)
Insert, Update or Delete \(N_v \cdot N_e \leq S\) \(O(1)\) \(O(1)\)
\(N_v \cdot N_e > S\) \(\left\{\begin{array}{c} O(1)\;...\;average \Omega(N_v\cdot N_e)\;...\;worst \end{array}\right.\)

I/O Analysis. Here, we denote the number of vertices as \(N\). Each vertex in the SparseArray has a size of \(S\). Each hash bucket can contain a maximum of \(M\) items, and we represent the vertex degree as \(N_e\), MaxHopDistance as \(H\), and the average number of versions per edge as \(N_v\).

Read the latest version: if \(N_v \cdot N_e \leq S\), the SparseArray contains fewer data items. A bounded scan of this small array, with complexity \(O(S)\), is extremely efficient due to its excellent data locality. When \(N_v \cdot N_e > S\), the data is stored in HopscotchHash Table, and the position of the most recent data item can be directly found using the LatestValuePtr, so the time complexity is \(O(1)\).

Read a specific historical version: SparseArray requires \(O(S)\), while HopscotchHash Table uses binary search with \(O(\log{N_v})\) due to sorted data and pointers.

Read historical versions for all objects: After identifying the first edge, subsequent versions can be found using NextOffsetInfo, resulting in \(O(N \cdot N_e)\) complexity.

Insert, Update or Delete: In SparseArray, insertion is \(O(1)\) due to direct access. In HopscotchHash Table, insertion is \(O(1)\) on average, but may require \(O(N_v \cdot N_e)\) in the worst case if resizing occurs. However, the hop failure probability is low, approximately \(\frac{1}{M \cdot H!}\), reducing this occurrence significantly.

Theorem 2. The probability of hop failure in HopscotchHash Table is  \(\frac{1}{M\cdot H!}\)

Please see the proofs in our github [24]. Finally, Table 1 summarizes the I/O characteristics of \(\textsf{TVA}\), focusing on its core components: the versioned ColumnStore with Temporal Table and the Hopscotch-optimized Hash Table.

5.3 SIMD Acceleration↩︎

Figure 8: Get a Specific Temporal Edge

To further improve query performance, \(\textsf{TVA}\) leverages Single Instruction, Multiple Data (SIMD) techniques. Next, we elaborate in detail how we utilize this technique to get an edge in our HopscotchHash Table. As discussed in § 4.2, the control array ControlInfo records the upper seven bits of the hash value along with a control bit, forming an 8-bit control field. Specifically, values from 0x00 to 0x7F indicate a valid entry and encode the upper seven hash bits; 0x80 denotes an empty slot; and 0xFF marks a deleted entry. This design allows for an initial assessment of whether a candidate slot may contain the target data item by simply comparing the 8-bit control field during query operations. Since each control field occupies only 1 byte, its small size allows it to be efficiently loaded into the cache, which reduces memory access latency.

We implement a SIMD-optimized binary search process within the GetTemporalEdge function, as shown in Algorithm 8. Instead of directly comparing full data items, it first calls the Match function (line 6) to rapidly filter a batch of candidates by matching their Control Fields. The Match function is where SIMD is applied (lines 21-26). It first loads 16 Control Fields into a SIMD register (line 22). Then, another instruction _mm_cmpeq_epi8 performs a parallel comparison of these 16 bytes against the target hash’s upper bits (line 24). The _mm_movemask_epi8 instruction efficiently converts the 16-byte comparison result into a 16-bit bitmap, identifying all matching candidates at once (line 25). After that, we compare the data items matched by the Match function (lines 8-17).

The application of SIMD technology in Temporal Property Storage lies in Version lookup, with the specific operational procedure being largely consistent with the above description. This approach allows \(\textsf{TVA}\) to perform an initial screening on multiple data items using just a few CPU instructions. It reduces the latency associated with traditional item-by-item comparisons and accelerates the location and retrieval of historical versions.

6 Implementation↩︎

\(\textsf{TVA}\) is implemented in C++ with \(\sim\)​7,000 lines of code. \(\textsf{TVA}\) realizes the hybrid architecture described above: it combines versioned columnar storage with an optimized Hopscotch hash table to enable efficient storage and querying of temporal graphs. \(\textsf{TVA}\) supports both property and non-property graph input formats. When the input is a non-property graph, \(\textsf{TVA}\) only utilizes Dynamic Topology Storage for the entire graph. \(\textsf{TVA}\) implements all temporal graph data management operations defined in § 2.1, providing users with rich interfaces to perform complex queries and updates directly on temporal graphs.

\(\textsf{TVA}\) supports Snapshot Isolation (SI). To ensure data consistency in concurrent environments, \(\textsf{TVA}\) uses fine-grained locking at the Header of each object’s Temporal Table and HopscotchHash Table, enabling thread-safe parallel updates and queries. This locking granularity avoids the performance bottlenecks caused by global locks, improves data access efficiency under multi-threaded workloads, and guarantees both atomicity and consistency of operations.

Fault Tolerance and Persistence. To ensure data durability, TVA implements a Write-Ahead Logging (WAL) mechanism. Each WAL entry is structured as a compact binary sequence starting with a header that contains a 4-byte magic number, version identifier, operation type and flags. This header is followed by an 8-byte timestamp, payload length, the variable-length payload, and finally a CRC32 checksum to guarantee data integrity during recovery. To address diverse storage constraints, TVA introduces a flexible persistence architecture managed via a PersistManager that supports two distinct modes. The Pointer Mode, designed for high-frequency access, retains TVA’s core in-memory structures (i.e., ColumnTable) to preserve traversal speed, but replaces bulky property values with lightweight 64-bit DiskOffset pointers referencing an append-only disk file. For scenarios requiring full persistence, the Full Mode leverages RocksDB as the backend. We design a specialized time-encoded key schema, specifically encoding keys as user:{uid}:ts:{timestamp} for vertices and friend:{src}:{dst}:ts:{timestamp} for edges, to exploit the lexicographical ordering of the underlying LSM-tree. This ensures that historical versions are physically adjacent on disk, enabling efficient point-in-time queries via iterator seeking and temporal range scans without loading the entire history into memory.

7 Evaluation↩︎

In this section, we evaluate \(\textsf{TVA}\) through comparisons with state-of-the-art graph systems. We conduct system-level and micro benchmarks to measure the query performance and storage consumption.

7.1 Experiment Setup↩︎

We conduct our experiments on a machine equipped with an Intel(R) Xeon(R) Gold 5220 CPU @ 2.20GHz and 128 GB of memory, running Ubuntu 20.04. All code is compiled using GCC 11.3.0 with the O3 optimization flag. All systems utilize 5 threads by default.

Competitors. We compare \(\textsf{TVA}\) with two categories of graph storage systems. 1) For temporal data operations, we compare with the most advanced graph data structures that support temporal functionality: Clock-G [16], T-GQL [15], and AeonG [17]. We also included PostgreSQL [25] (representing temporal-extended RDBMS) and RocksDB [26] (representing Key-Value stores) in our experiments. Note that both Clock-G and T-GQL store all vertices and edges in memory. To ensure a fair comparison, we cache all data in memory for all the evaluations. 2) For recent data operations, we select state-of-the-art dynamic graph storage systems as baselines for comparison: GraphOne [13], Stinger [12], and Sortledton [14]. Since these structures do not support temporal operation. To ensure a fair comparison for MVCC graph stores, we standardize the experimental setup by maintaining complete version histories across all compared systems to rigorously evaluate their temporal query capabilities without the interference of garbage collection.

Graph Datasets. We employ a variety of graph datasets for evaluation, including: IMDB [27], an actor collaboration network from IMDB; DBLP [28], a collaboration network based on DBLP; YouTube [28], a social network from YouTube; Epinions [29], the Epinions “who-trust-whom” social network; Pokec [30], Slovakia’s most popular social network; and LDBC SNB [31], the LDBC Social Network Benchmark designed to simulate real-world social networks.

Graph Benchmark. For temporal queries, we utilize three temporal workloads: T-mgBench, T-LDBC, and T-gMark, following [17]. T-mgBench is based on the Pokec dataset and follows the Memgraph mgBench workload. Specifically, the “FOR TT AS OF \(t\)” clause is appended to queries Q1 and Q3 to generate “time point” queries, while the “FOR TT FROM \(t_1\) TO \(t_2\)” clause is included in Q2 and Q4 to produce temporal range queries. T-LDBC is adapted from the LDBC workload by integrating “FOR TT AS OF \(t\)” into the IS queries (IS1–IS7). T-gMark is based on gMark [32] and is designed to stress-test property management. It features 24 vertex properties and 82 edge properties, covering diverse query shapes and selectivities.

For recent data operations, we consider four representative graph analysis algorithms, including Breadth-First Search (BFS), Single-Source Shortest Path (SSSP), PageRank (PR), and Weakly Connected Components (WCC).

7.2 Experiment on Temporal Graph↩︎

7.2.1 Experiments on temporal graph storage consumption.↩︎

Figure 9: Temporal Graph Storage Consumption and Graph Operation Latency.
Figure 10: Comparisons on Temporal Query Latency.

We first use T-mgBench to analyze storage consumption under varying numbers of graph operations, as shown in Figure 9 (a). The results show that \(\textsf{TVA}\) demonstrates the lowest storage overhead across different operation volumes. \(\textsf{TVA}\)’s storage efficiency surpasses AeonG by up to \(2.2\times\), Clock-G by up to \(3.6\times\), and T-GQL by up to \(4.7\times\). These storage savings primarily result from \(\textsf{TVA}\)’s compact storage structure. The system efficiently organizes historical data in the Temporal Buffer and employs a low-overhead structure to manage version metadata, thereby minimizing the overhead associated with maintaining historical data. In contrast, AeonG periodically stores full data within the version chain; T-GQL maintains complete information for all data; and Clock-G periodically creates full historical snapshots of the graph. All these approaches lead to higher storage overhead compared to \(\textsf{TVA}\).

The results on the T-LDBC dataset, shown in Figure 9 (c), further demonstrate this advantage and provide a clearer breakdown of storage composition. \(\textsf{TVA}\) achieves up to \(1.2\times\), \(6.7\times\), and \(4.3\times\) lower storage consumption than AeonG, Clock-G, and T-GQL, respectively. The total storage required by \(\textsf{TVA}\) remains minimal, with the “Historical Data” component being notably small in comparison to the “Current Data”. In contrast, for Clock-G, the Historical Data constitutes a large proportion of the total storage. These results indicate that Clock-G’s approach of periodically creating full historical snapshots substantially increases storage overhead.

7.2.2 Experiments on temporal graph operation latency.↩︎

To evaluate the performance of temporal graph operations, we measured the average latency on the T-mgBench and T-LDBC, with the results presented in Figure 9 (b) and Figure 9 (d).

Figure 9 (b) shows that on T-mgBench, \(\textsf{TVA}\) consistently maintains the lowest and most stable average graph operation latency as the number of operations increases from 80k to 400k. In contrast, other systems exhibit increased high latency. At 400k operations, \(\textsf{TVA}\)’s average latency is \(3\times\), \(3.1\times\), and \(1197.6\times\) faster than AeonG, Clock-G, and T-GQL. This advantage is primarily due to \(\textsf{TVA}\)’s efficient historical data management. Specifically, \(\textsf{TVA}\) avoids the overhead caused by AeonG’s approach of archiving the latest data to a historical storage area and recording changes via a version chain, which introduces additional computational cost. Meanwhile, T-GQL suffers from inefficient traversal because it does not distinguish between current and historical data.

Subsequent experiments on the larger and more complex T-LDBC dataset, shown in Figure 9 (d), further corroborate \(\textsf{TVA}\)’s low-latency advantage. \(\textsf{TVA}\) records the lowest average operation latency, with its performance being \(2.9\times\) faster than AeonG, \(8.1\times\) faster than Clock-G, and \(28.9\times\) faster than T-GQL. Notably, on the large-scale T-LDBC graph, Clock-G’s periodic creation of large historical snapshots consumes significant CPU and I/O resources. This resource contention adversely impacts its real-time graph operation performance, explaining its relatively higher latency.

7.2.3 Experiments on temporal graph analysis performance.↩︎

We first evaluate temporal query performance using T-mgBench (Q1–Q4). As shown in Figure 10 (a)-(c), \(\textsf{TVA}\) consistently outperforms AeonG, Clock-G, and T-GQL across all queries, achieving average latency reductions of up to \(206.9\times\). This advantage stems primarily from our Temporal Table enabling efficient direct access to historical data, and our Hopscotch-based algorithm ensuring contiguous storage to minimize unnecessary scans. In contrast, AeonG suffers from traversing long version chains, Clock-G incurs overhead from aggregating multiple snapshots, and T-GQL performs expensive full-graph traversals. Furthermore, \(\textsf{TVA}\) maintains this superior performance under varying workloads, outperforming competitors even as the volume of graph operations scales up.

To validate the effectiveness of \(\textsf{TVA}\) in larger-scale and more complex scenarios, we conducted evaluations on T-LDBC. As shown in Figures 10 (d) and (e), \(\textsf{TVA}\) continues to demonstrate superior performance across various query types, achieving latency reductions of up to \(4.4\times\), \(6.6\times\), and \(207.1\times\) compared to AeonG, Clock-G, and T-GQL, respectively. To further stress-test the systems, we evaluated an even larger graph containing 9.28 million vertices and 52.7 million edges. During this experiment, T-GQL failed to complete the test due to an Out-Of-Memory error. The results for the remaining systems are shown in Figure 10 (f), where \(\textsf{TVA}\) achieves performance improvements of up to \(4.7\times\) and \(83.9\times\) over AeonG and Clock-G.

To justify the necessity of a specialized temporal graph store over general-purpose systems, we compared TVA against PostgreSQL (representing temporal-extended RDBMS) and RocksDB (representing Key-Value stores). The results in Figure 10 (g) and (h) reveal fundamental architectural bottlenecks in these baselines. PostgreSQL relies on expensive join operations to traverse edges, resulting in a combinatorial explosion of intermediate results and a sharp decline in performance for multi-hop temporal queries. By utilizing a version-aware storage layout that replaces costly joins with direct pointer dereferencing, TVA achieves a \(126.4\times\) performance improvement over PostgreSQL. Similarly, although RocksDB is efficient for simple point queries, it lacks structural awareness, leading to excessive random I/O and inefficient scans during the multi-hop traversals and temporal range scans required by time-series graph workloads.

To address concerns about system overhead in non-temporal scenarios, we compared the performance of TVA and baseline systems on mgBench with all temporal data excluded. The results in Figure 10 (i) indicate that while all systems naturally experience a performance boost due to the elimination of version retrieval steps, TVA maintains its superior architectural efficiency, achieving a \(21.7\times\) performance improvement over AeonG on static snapshots.

Furthermore, to evaluate property-intensive workloads on T-gMark, Figure 10 (j) shows TVA’s architecture decouples property values from version metadata, enabling parallel retrieval of multiple properties once the version metadata is located in the Temporal Table. This yields speedups of \(5.1\times\), \(132.9\times\), and \(33.4\times\) over AeonG, Clock-G, and T-GQL, respectively.

7.3 Experiment on Current Graph↩︎

Figure 11: Current Graph Storage Consumption and Graph Operation Throughput.

7.3.1 Experiments on current graph storage consumption.↩︎

We first evaluate the current data storage overhead of \(\textsf{TVA}\). The results are shown in Figure 11 (a). The results demonstrate that \(\textsf{TVA}\) exhibits superior storage efficiency on the majority of datasets. Compared to the baseline systems, \(\textsf{TVA}\)’s space utilization is up to \(1.4\times\), \(1.8\times\), and \(5.1\times\) higher than Sortledton, GraphOne, and Stinger, respectively. An exception occurs on the IMDB and Epinions datasets, where \(\textsf{TVA}\)’s storage overhead is slightly higher than that of Sortledton. This is attributed to \(\textsf{TVA}\)’s storage management strategy, which pre-allocates fixed-size space for low-degree vertices. When the graph contains a large number of vertices with very low degrees, this strategy can lead to some wasted space. We consider this overhead acceptable because the reserved space is a key design choice that enables high insertion throughput for low-degree vertices.

7.3.2 Experiments on current graph operation latency.↩︎

We then evaluate the insertion performance of different graph systems. Figure 11 (b) shows the insertion throughput across multiple datasets. On the YouTube dataset, GraphOne marginally outperforms \(\textsf{TVA}\), mainly because most edges connect to a few high-degree vertices, causing more hash collisions during position lookups. Notably, GraphOne skips edge existence checks in this experiment; enabling such checks would reduce its throughput to about 5 edges per second [33], due to the overhead of snapshot creation. On all other datasets, \(\textsf{TVA}\) consistently achieves up to \(6.1\times\), \(2\times\), and \(4.5\times\) higher insertion throughput than Sortledton, GraphOne, and Stinger. This can be attributed to \(\textsf{TVA}\)’s hybrid insertion approach: it pre-allocates space for low-degree vertices to avoid frequent memory operations and utilizes a HopscotchHash Table for fast indexing of high-degree vertices, supporting efficient edge insertions. These features highlight \(\textsf{TVA}\)’s adaptability to diverse graph structures.

7.3.3 Experiments on current graph query latency.↩︎

We further evaluate the performance of typical graph analysis tasks by measuring the execution times of four graph analysis algorithms, and compare \(\textsf{TVA}\) with its competitors. Figure 12 presents the normalized execution times of different systems across various datasets, where the execution time of \(\textsf{TVA}\) is set as the baseline and normalized to 1. \(\textsf{TVA}\) outperforms other methods in most cases, achieving speedups of up to \(6.5\times\), \(18.4\times\), and \(5.6\times\) compared to Sortledton, GraphOne, and Stinger. GraphOne suffers the largest performance decline during graph analysis due to its block-based adjacency list operations, which cause substantial random access. Sortledton outperforms others in BFS because its skiplist structure makes it convenient to access all neighbors of a vertex, while \(\textsf{TVA}\) incurs extra overhead from hash table lookups during edge traversal. However, this approach is less effective for algorithms such as PR and SSSP that require frequent access to neighbors of different vertices.

Figure 12: Current Graph Query Latency.

7.4 Persistence Analysis↩︎

7.4.1 Effectiveness of TemporalChain in Long Version Chains.↩︎

We evaluate TemporalChain for querying items at a specific timestamp. In Figure 13 (a), TemporalChain achieves up to \(6.2\times\) speedup over independent item lookups by leveraging inter-item links to directly navigate to subsequent relevant versions. By confining the search scope within a bounded neighborhood and utilizing TemporalChain to bypass repeated full-chain traversals, \(\textsf{TVA}\) effectively circumvents the linear performance degradation (\(O(N_v)\)) typical of traditional linked-list version-chain approaches. Our design ensures that version retrieval time remains logarithmic (\(O(\log N_v)\)), making \(\textsf{TVA}\)’s computational advantage increasingly prominent under heavy, history-intensive workloads.

7.4.2 Persistence Overhead.↩︎

To rigorously quantify the cost of durability and I/O overhead when datasets exceed memory capacity, we evaluate \(\textsf{TVA}\) under two distinct memory-spillover persistence strategies managed by the PersistManager: TVA_1 (Pointer Mode) and TVA_2 (Full Mode). Figures 13 (b) and (c) detail the storage footprint and operation latency. The Pointer Mode retains lightweight topology metadata in RAM while offloading bulky historical property values to an append-only disk file via 64-bit offsets, thus reducing the memory footprint. The Full Mode fully persists both topology and properties by leveraging RocksDB as a backend, utilizing a specialized time-encoded key schema (e.g., user:uid:ts:timestamp) to exploit LSM-tree lexicographical ordering so that historical versions remain physically adjacent on disk. Furthermore, we explicitly measured the overhead of our Write-Ahead Logging (WAL) mechanism. By serializing both version metadata and actual data into a highly compact binary log format—featuring a lightweight header and CRC32 checksum for data integrity—our persistence mechanism introduces only marginal latency penalties. The results confirm \(\textsf{TVA}\) maintains robust, competitive throughput even under strict durability guarantees.

Figure 13: Persistence Analysis on \textsf{TVA}.

7.5 Ablation Study on \(\textsf{TVA}\)↩︎

7.5.1 Effectiveness of HopscotchTable.↩︎

We evaluate the HopscotchHash Table’s hop distance and query efficiency. Figure 14 (a) shows that under varying Zipf skewness (\(\alpha\)), the hop distance for 99% of data insertions peaks at 8 and strictly remains below 16. This ensures versions are localized within a bounded, SIMD-friendly neighborhood. Compared to a standard MVCC version-chain baseline [34], Figure 14 (b) demonstrates that HopscotchHash Table achieves up to \(2.9\times\) speedup as the history length grows, reducing lookup complexity to \(O(\log N_v)\) via binary search.

7.5.2 Effectiveness of SIMD↩︎

As shown in Figure 14 (c), leveraging SIMD instructions provides up to \(1.4\times\) query speedup by enabling parallel 16-byte candidate filtering. While the relative performance gap narrows with longer historical version chains and larger data volumes, SIMD remains a valuable optimization for rapid candidate screening when items have fewer historical versions.

7.5.3 Effectiveness of Migration Mechanism.↩︎

We migrate a vertex to the HopscotchHashTable when either its degree exceeds \(T_{deg}\) or its update frequency exceeds \(T_{ver}\); Figure 14 (d) identifies the optimal boundary via normalized runtime across varying degrees. To avoid latency penalties, migration runs asynchronously: the triggering write flags the vertex and completes without blocking, while a background worker allocates a new bucket, re-organizes the edges, and atomically updates the pointer.

7.5.4 Scalability and Update Performance.↩︎

To explicitly demonstrate \(\textsf{TVA}\)’s concurrency and scalability under update-intensive conditions, we evaluated mixed read-write workloads. Figure 14 (e) illustrates that \(\textsf{TVA}\) achieves near-linear throughput scaling as thread counts increase. Furthermore, Figure 14 (f) utilizes varying write-read ratios to simulate realistic update-intensive workloads in concurrent operations. The results verify that our fine-grained Snapshot Isolation (SI) implementation, which locks only at the individual object’s Temporal Table Header rather than globally, effectively minimizes lock contention. This allows \(\textsf{TVA}\) to maintain high query throughput without blocking readers, even under severe update pressure.

Figure 14: Performance Analysis on \textsf{TVA}.

8 Related Works↩︎

Temporal Graph Management. Recently, many works focus on managing and querying temporal graph data. AeonG [17] extends graph databases with temporal support, but it cannot completely address the disadvantages posed by version chain scanning. Snapshot-based systems such as T-GQL [15] provide clear historical models but suffer from high storage costs and inefficient query performance due to full snapshot reconstruction. Kineograph [35] also explores fast-changing graphs, but with a focus on distributed environments and only capturing recent changes.

Multi-Version Database Systems. Classic multi-version database systems (MVCC) were primarily designed for high-concurrency transactional workloads, where historical versions are side effects of isolation rather than a persistent analytical asset [34]. Consequently, MVCC systems typically assume version chains are short and rely on aggressive garbage collection or vacuuming to remove obsolete versions and control storage growth and lookup overhead [36], [37]. In contrast, temporal graph databases must preserve long histories for time snapshot and temporal-range analytics, and their workloads further stress version management, making strong temporal locality across scans critical.

General-purpose Storage Systems. Temporal graphs can be mapped to relational tables such as vertex and edge tables and queried with relational operators, and modern RDBMSs also provide temporal features [25], [26]. However, even with temporal extensions, relational execution for multi-hop traversals typically requires repeated joins, which can cause a combinatorial blow-up as hop count increases. Likewise, KV stores excel at point lookups but lack structural awareness of graph topology. As a result, multi-hop traversals and temporal range scans suffer from excessive random accesses and poor locality.

9 Conclusion↩︎

This paper presented \(\textsf{TVA}\), an efficient temporal graph storage system. \(\textsf{TVA}\) decoupled version metadata from actual data, and based on this principle, we designed tailored data structures, i.e., the temporal table and the enhanced hopscotch-based hash table, to enable fast version retrieval. Further, we introduced a version-skipping approach and leveraged SIMD parallelism to support high-performance temporal query processing. Experimental results demonstrated that \(\textsf{TVA}\) achieves superior performance in terms of temporal query latency, update efficiency, and storage compactness compared to state-of-the-art graph storage systems.

This work is supported by the National Natural Science Foundation of China under Grant 62441230, 62461146205, 62072458 and 62472429.

References↩︎

[1]
Y. Xia, Y. Fang, and W. Luo, “Efficiently counting triangles in large temporal graphs,” Proc. ACM Manag. Data, vol. 3, no. 1, pp. 38:1–38:27, 2025.
[2]
W. Han et al., “Chronos: A graph engine for temporal graph analysis,” in EuroSys, 2014, pp. 1:1–1:14.
[3]
D. Cheng, X. Wang, Y. Zhang, and L. Zhang, “Graph neural network for fraud detection via spatial-temporal attention,” IEEE Trans. Knowl. Data Eng., vol. 34, no. 8, pp. 3800–3813, 2022.
[4]
S. Cao, X. Yang, C. Chen, J. Zhou, X. Li, and Y. Qi, “TitAnt: Online real-time transaction fraud detection in ant financial,” Proc. VLDB Endow., vol. 12, no. 12, pp. 2082–2093, 2019.
[5]
C. Ye, Y. Li, B. He, Z. Li, and J. Sun, “GPU-accelerated graph label propagation for real-time fraud detection,” in SIGMOD conference, 2021, pp. 2348–2356.
[6]
C. Wilson, B. Boe, A. Sala, K. P. N. Puttaswamy, and B. Y. Zhao, “User interactions in social networks and their implications,” in EuroSys, 2009, pp. 205–218.
[7]
D. B. Lomet, R. S. Barga, M. F. Mokbel, G. Shegalov, R. Wang, and Y. Zhu, “Immortal DB: Transaction time support for SQL server,” in SIGMOD conference, 2005, pp. 939–941.
[8]
Y. Miao et al., “ImmortalGraph: A system for storage and analysis of temporal graphs,” ACM Trans. Storage, vol. 11, no. 3, pp. 14:1–14:34, 2015.
[9]
S. Xiang, G. Zhang, D. Cheng, and Y. Zhang, “Enhancing attribute-driven fraud detection with risk-aware graph representation,” IEEE Trans. Knowl. Data Eng., vol. 37, no. 5, pp. 2501–2512, 2025.
[10]
F. Xiao, Y. Wu, M. Zhang, G. Chen, and B. C. Ooi, MINT: Detecting fraudulent behaviors from time-series relational data,” Proc. VLDB Endow., vol. 16, no. 12, pp. 3610–3623, 2023.
[11]
J. Jiang, S. Yao, Y. Li, Q. Wang, B. He, and M. Chen, “Dupin: A parallel framework for densest subgraph discovery in fraud detection on massive graphs,” Proceedings of the ACM on Management of Data, vol. 3, no. 3, pp. 1–26, 2025.
[12]
D. Ediger, R. McColl, E. J. Riedy, and D. A. Bader, STINGER: High performance data structure for streaming graphs,” in HPEC, 2012, pp. 1–5.
[13]
P. Kumar and H. H. Huang, “GraphOne: A data store for real-time analytics on evolving graphs,” in FAST, 2019, pp. 249–263.
[14]
P. Fuchs, J. Giceva, and D. Margan, “Sortledton: A universal, transactional graph data structure,” Proc. VLDB Endow., vol. 15, no. 6, pp. 1173–1186, 2022.
[15]
A. Debrouvier, E. Parodi, M. Perazzo, V. Soliani, and A. A. Vaisman, “A model and query language for temporal graph databases,” VLDB J., vol. 30, no. 5, pp. 825–858, 2021.
[16]
M. Massri, Z. Miklós, P. R. Parvédy, and P. Meye, “Clock-g: A temporal graph management system with space-efficient storage technique,” in ICDE, 2022, pp. 2263–2276.
[17]
J. Hou et al., “AeonG: An efficient built-in temporal support in graph databases,” Proc. VLDB Endow., vol. 17, no. 6, pp. 1515–1527, 2024.
[18]
G. Malewicz et al., “Pregel: A system for large-scale graph processing,” in SIGMOD conference, 2010, pp. 135–146.
[19]
M. Herlihy, N. Shavit, and M. Tzafrir, “Hopscotch hashing,” in DISC, 2008, vol. 5218, pp. 350–364.
[20]
D. J. Abadi, S. Madden, and N. Hachem, “Column-stores vs. Row-stores: How different are they really?” in SIGMOD conference, 2008, pp. 967–980.
[21]
M. Raasveldt and H. Mühleisen, “Duckdb: An embeddable analytical database,” in Proceedings of the 2019 international conference on management of data, 2019, pp. 1981–1984.
[22]
W. Zhang et al., BG3: A cost effective and I/O efficient graph database in bytedance,” in SIGMOD conference companion, 2024, pp. 360–372.
[23]
S. Yu et al., “LSMGraph: A high-performance dynamic graph storage system with multi-level CSR,” Proc. ACM Manag. Data, vol. 2, no. 6, pp. 243:1–243:28, 2024.
[24]
Wenhao Li, “TVA.” https://github.com/Sakuraaa0/TVA, 2026.
[25]
PostgreSQL wiki, Accessed: 2026-01Temporal Extensions – PostgreSQL Wiki.” https://wiki.postgresql.org/wiki/Temporal_Extensions, 2025.
[26]
Facebook, RocksDB.” https://github.com/facebook/rocksdb, 2016.
[27]
R. A. Rossi and N. K. Ahmed, “The network data repository with interactive graph analytics and visualization,” in AAAI, 2015, [Online]. Available: https://networkrepository.com.
[28]
J. Yang and J. Leskovec, “Defining and evaluating network communities based on ground-truth,” in ICDM, 2012, pp. 745–754.
[29]
O. Sacco, J. G. Breslin, and S. Decker, “Fine-grained trust assertions for privacy management in the social semantic web,” in TrustCom/ISPA/IUCC, 2013, pp. 218–225.
[30]
N. D. Soares, R. Braga, J. M. N. David, K. B. Siqueira, and V. Ströele, “Data analysis in social networks for agribusiness - A systematic mapping study,” CoRR, vol. abs/2208.14807, 2022.
[31]
O. Erling et al., “The LDBC social network benchmark: Interactive workload,” in SIGMOD conference, 2015, pp. 619–630.
[32]
G. Bagan, A. Bonifati, R. Ciucanu, G. H. L. Fletcher, A. Lemay, and N. Advokaat, “gMark: Schema-driven generation of graphs and queries,” IEEE Trans. Knowl. Data Eng., vol. 29, no. 4, pp. 856–869, 2017.
[33]
D. D. Leo and P. A. Boncz, “Teseo and the analysis of structural dynamic graphs,” Proc. VLDB Endow., vol. 14, no. 6, pp. 1053–1066, 2021.
[34]
J.-B. Kim, K. Kim, H. Cho, J. Yu, S. Kang, and H. Jung, “Rethink the scan in MVCC databases,” in SIGMOD ’21: International conference on management of data, virtual event, china, june 20-25, 2021, 2021, pp. 938–950, doi: 10.1145/3448016.3452783.
[35]
R. Cheng et al., “Kineograph: Taking the pulse of a fast-changing and connected world,” in Proceedings of the 7th ACM european conference on computer systems, 2012, pp. 85–98.
[36]
M. Zhang, Y. Hua, and Z. Yang, “Motor: Enabling multi-versioning for distributed transactions on disaggregated memory,” in 18th USENIX symposium on operating systems design and implementation, OSDI 2024, santa clara, CA, USA, july 10-12, 2024, 2024, pp. 801–819, [Online]. Available: https://www.usenix.org/conference/osdi24/presentation/zhang-ming.
[37]
Y. Wu, J. Arulraj, J. Lin, R. Xian, and A. Pavlo, “An empirical evaluation of in-memory multi-version concurrency control,” Proc. VLDB Endow., vol. 10, no. 7, pp. 781–792, 2017, doi: 10.14778/3067421.3067427.

  1. Wei Lu is the corresponding author.
    This work is licensed under the Creative Commons BY-NC-ND 4.0 International License. Visit https://creativecommons.org/licenses/by-nc-nd/4.0/ to view a copy of this license. For any use beyond those covered by this license, obtain permission by emailing . Copyright is held by the owner/author(s). Publication rights licensed to the VLDB Endowment.

    Proceedings of the VLDB Endowment, Vol. 19, No. 10ISSN 2150-8097.
    doi:10.14778/3828612.3828613
    ↩︎