Skip to content

Commit 4215ba0

Browse files
authored
feat: added new database.getSize() api (#3045)
1 parent 1b4ba75 commit 4215ba0

7 files changed

Lines changed: 230 additions & 2 deletions

File tree

engine/src/main/java/com/arcadedb/database/BasicDatabase.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ public interface BasicDatabase extends AutoCloseable {
3535

3636
String getDatabasePath();
3737

38+
/**
39+
* Returns the total size of the database in bytes, including all database files
40+
* (data files, indexes, transaction logs, metadata, etc.).
41+
*
42+
* @return The total size in bytes of all files in the database directory
43+
*/
44+
long getSize();
45+
3846
boolean isOpen();
3947

4048
Schema getSchema();

engine/src/main/java/com/arcadedb/database/DatabaseInternal.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ default TransactionContext getTransaction() {
5151
return tx;
5252
}
5353

54+
long getSize();
55+
5456
TransactionContext getTransactionIfExists();
5557

5658
MutableEmbeddedDocument newEmbeddedDocument(EmbeddedModifier modifier, String typeName);

engine/src/main/java/com/arcadedb/database/LocalDatabase.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import java.util.concurrent.atomic.*;
9292
import java.util.concurrent.locks.*;
9393
import java.util.logging.*;
94+
import java.util.stream.*;
9495

9596
/**
9697
* Local implementation of {@link Database}. It is based on files opened on the local file system.
@@ -324,6 +325,29 @@ public String getDatabasePath() {
324325
return databasePath;
325326
}
326327

328+
@Override
329+
public long getSize() {
330+
return executeInReadLock(() -> {
331+
checkDatabaseIsOpen();
332+
try {
333+
final Path dir = Path.of(databasePath);
334+
if (!Files.exists(dir))
335+
return 0L;
336+
try (Stream<Path> stream = Files.walk(dir)) {
337+
return stream.filter(Files::isRegularFile).mapToLong(p -> {
338+
try {
339+
return Files.size(p);
340+
} catch (IOException e) {
341+
throw new UncheckedIOException(e);
342+
}
343+
}).sum();
344+
}
345+
} catch (UncheckedIOException e) {
346+
throw new DatabaseOperationException("Error calculating database size", e.getCause());
347+
}
348+
});
349+
}
350+
327351
@Override
328352
public String getCurrentUserName() {
329353
final DatabaseContext.DatabaseContextTL dbContext = DatabaseContext.INSTANCE.getContextIfExists(databasePath);
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright © 2021-present Arcade Data Ltd ([email protected])
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-FileCopyrightText: 2021-present Arcade Data Ltd ([email protected])
17+
* SPDX-License-Identifier: Apache-2.0
18+
*/
19+
package com.arcadedb.database;
20+
21+
import com.arcadedb.TestHelper;
22+
import com.arcadedb.graph.MutableVertex;
23+
import com.arcadedb.schema.DocumentType;
24+
import com.arcadedb.schema.Type;
25+
import org.junit.jupiter.api.Test;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Test cases for Database.getSize() API
31+
*
32+
* @author Luca Garulli ([email protected])
33+
*/
34+
class DatabaseGetSizeTest extends TestHelper {
35+
36+
@Test
37+
void testGetSizeOnEmptyDatabase() {
38+
// Even an empty database has metadata files (schema, configuration, etc.)
39+
final long initialSize = database.getSize();
40+
assertThat(initialSize).isGreaterThan(0L);
41+
}
42+
43+
@Test
44+
void testGetSizeIncreasesWithDocuments() {
45+
database.transaction(() -> {
46+
// Create a document type
47+
final DocumentType personType = database.getSchema().createDocumentType("Person");
48+
personType.createProperty("name", Type.STRING);
49+
personType.createProperty("age", Type.INTEGER);
50+
personType.createProperty("email", Type.STRING);
51+
});
52+
53+
final long sizeBeforeInsert = database.getSize();
54+
55+
// Insert documents
56+
database.transaction(() -> {
57+
for (int i = 0; i < 1000; i++) {
58+
final MutableDocument doc = database.newDocument("Person");
59+
doc.set("name", "Person" + i);
60+
doc.set("age", 20 + (i % 50));
61+
doc.set("email", "person" + i + "@example.com");
62+
doc.save();
63+
}
64+
});
65+
66+
final long sizeAfterInsert = database.getSize();
67+
68+
// Database size should increase after inserting documents
69+
assertThat(sizeAfterInsert).isGreaterThan(sizeBeforeInsert);
70+
}
71+
72+
@Test
73+
void testGetSizeIncreasesWithVerticesAndEdges() {
74+
database.transaction(() -> {
75+
// Create vertex and edge types
76+
database.getSchema().createVertexType("User");
77+
database.getSchema().createEdgeType("Follows");
78+
});
79+
80+
final long sizeBeforeData = database.getSize();
81+
82+
// Create vertices and edges
83+
database.transaction(() -> {
84+
MutableVertex user1 = database.newVertex("User");
85+
user1.set("name", "Alice");
86+
user1.save();
87+
88+
MutableVertex user2 = database.newVertex("User");
89+
user2.set("name", "Bob");
90+
user2.save();
91+
92+
MutableVertex user3 = database.newVertex("User");
93+
user3.set("name", "Charlie");
94+
user3.save();
95+
96+
// Create edges (using explicit method to avoid ambiguity)
97+
user1.newEdge("Follows", user2, true, (Object[]) null);
98+
user2.newEdge("Follows", user3, true, (Object[]) null);
99+
user1.newEdge("Follows", user3, true, (Object[]) null);
100+
});
101+
102+
final long sizeAfterData = database.getSize();
103+
104+
// Database size should increase after inserting vertices and edges
105+
assertThat(sizeAfterData).isGreaterThan(sizeBeforeData);
106+
}
107+
108+
@Test
109+
void testGetSizeWithIndex() {
110+
database.transaction(() -> {
111+
// Create a document type with an indexed property
112+
final DocumentType productType = database.getSchema().createDocumentType("Product");
113+
productType.createProperty("sku", Type.STRING);
114+
productType.createProperty("name", Type.STRING);
115+
productType.createProperty("price", Type.DOUBLE);
116+
117+
// Create an index
118+
database.getSchema().createTypeIndex(com.arcadedb.schema.Schema.INDEX_TYPE.LSM_TREE, true, "Product", "sku");
119+
});
120+
121+
final long sizeBeforeInsert = database.getSize();
122+
123+
// Insert documents that will be indexed
124+
database.transaction(() -> {
125+
for (int i = 0; i < 500; i++) {
126+
final MutableDocument doc = database.newDocument("Product");
127+
doc.set("sku", "SKU-" + String.format("%05d", i));
128+
doc.set("name", "Product " + i);
129+
doc.set("price", 10.0 + i);
130+
doc.save();
131+
}
132+
});
133+
134+
final long sizeAfterInsert = database.getSize();
135+
136+
// Database size should increase (includes both data and index files)
137+
assertThat(sizeAfterInsert).isGreaterThan(sizeBeforeInsert);
138+
}
139+
140+
@Test
141+
void testGetSizeMultipleCalls() {
142+
// Multiple calls to getSize() should return consistent results
143+
final long size1 = database.getSize();
144+
final long size2 = database.getSize();
145+
final long size3 = database.getSize();
146+
147+
assertThat(size1).isEqualTo(size2);
148+
assertThat(size2).isEqualTo(size3);
149+
}
150+
151+
@Test
152+
void testGetSizeAfterDelete() {
153+
database.transaction(() -> {
154+
database.getSchema().createDocumentType("TempDoc");
155+
});
156+
157+
// Insert documents
158+
database.transaction(() -> {
159+
for (int i = 0; i < 100; i++) {
160+
final MutableDocument doc = database.newDocument("TempDoc");
161+
doc.set("value", "Data" + i);
162+
doc.save();
163+
}
164+
});
165+
166+
final long sizeAfterInsert = database.getSize();
167+
168+
// Delete some documents
169+
database.transaction(() -> {
170+
database.command("sql", "DELETE FROM TempDoc WHERE value LIKE 'Data5%'").close();
171+
});
172+
173+
final long sizeAfterDelete = database.getSize();
174+
175+
// Size might not decrease immediately due to space not being reclaimed until compaction
176+
// But we can verify the size is still valid
177+
assertThat(sizeAfterDelete).isGreaterThan(0L);
178+
}
179+
}

engine/src/test/java/com/arcadedb/query/sql/method/collection/SQLMethodTransformTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.arcadedb.database.DocumentCallback;
2626
import com.arcadedb.database.DocumentIndexer;
2727
import com.arcadedb.database.EmbeddedModifier;
28+
import com.arcadedb.database.LocalTransactionExplicitLock;
2829
import com.arcadedb.database.MutableDocument;
2930
import com.arcadedb.database.MutableEmbeddedDocument;
3031
import com.arcadedb.database.RID;
@@ -33,7 +34,6 @@
3334
import com.arcadedb.database.RecordEvents;
3435
import com.arcadedb.database.RecordFactory;
3536
import com.arcadedb.database.TransactionContext;
36-
import com.arcadedb.database.LocalTransactionExplicitLock;
3737
import com.arcadedb.database.async.DatabaseAsyncExecutor;
3838
import com.arcadedb.database.async.ErrorCallback;
3939
import com.arcadedb.database.async.OkCallback;
@@ -137,6 +137,11 @@ public Record invokeAfterReadEvents(final Record record) {
137137
return record;
138138
}
139139

140+
@Override
141+
public long getSize() {
142+
return 0L;
143+
}
144+
140145
@Override
141146
public TransactionContext getTransactionIfExists() {
142147
return null;

server/src/main/java/com/arcadedb/server/ServerDatabase.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public String getDatabasePath() {
106106
return wrapped.getDatabasePath();
107107
}
108108

109+
@Override
110+
public long getSize() {
111+
return wrapped.getSize();
112+
}
113+
109114
@Override
110115
public String getCurrentUserName() {
111116
return wrapped.getCurrentUserName();

server/src/main/java/com/arcadedb/server/ha/ReplicatedDatabase.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.arcadedb.database.DocumentIndexer;
2929
import com.arcadedb.database.EmbeddedModifier;
3030
import com.arcadedb.database.LocalDatabase;
31+
import com.arcadedb.database.LocalTransactionExplicitLock;
3132
import com.arcadedb.database.MutableDocument;
3233
import com.arcadedb.database.MutableEmbeddedDocument;
3334
import com.arcadedb.database.RID;
@@ -36,7 +37,6 @@
3637
import com.arcadedb.database.RecordEvents;
3738
import com.arcadedb.database.RecordFactory;
3839
import com.arcadedb.database.TransactionContext;
39-
import com.arcadedb.database.LocalTransactionExplicitLock;
4040
import com.arcadedb.database.async.DatabaseAsyncExecutor;
4141
import com.arcadedb.database.async.ErrorCallback;
4242
import com.arcadedb.database.async.OkCallback;
@@ -347,6 +347,11 @@ public String getDatabasePath() {
347347
return proxied.getDatabasePath();
348348
}
349349

350+
@Override
351+
public long getSize() {
352+
return proxied.getSize();
353+
}
354+
350355
@Override
351356
public String getCurrentUserName() {
352357
return proxied.getCurrentUserName();

0 commit comments

Comments
 (0)