Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IGNITE-20429 Dump check command implemented #10949

Merged
merged 106 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
18f8278
IGNITE-19950 WIP
nizhikov Jul 12, 2023
80e8b84
IGNITE-19950 WIP
nizhikov Jul 12, 2023
e2b9264
IGNITE-19950 WIP
nizhikov Jul 18, 2023
e698ed6
IGNITE-19950 WIP
nizhikov Jul 27, 2023
9ba0280
IGNITE-19950 WIP
nizhikov Jul 27, 2023
7300b47
IGNITE-19950 Save metadata implemented
nizhikov Jul 27, 2023
dcb9e81
IGNITE-19950 Dump creation process implemented
nizhikov Jul 28, 2023
be062a7
IGNITE-19950 Dump creation process implemented
nizhikov Jul 28, 2023
b275a98
IGNITE-19950 Stub for dump creation process implemented
nizhikov Jul 28, 2023
42c1489
Merge branch 'master' into IGNITE-19950-snapshot-merge
nizhikov Aug 11, 2023
370edae
Merge branch 'master' into IGNITE-19950-snapshot-merge
nizhikov Aug 16, 2023
a55bc15
IGNITE-19950 WIP
nizhikov Aug 21, 2023
8f0b873
Merge branch 'master' into IGNITE-19950-snapshot-merge
nizhikov Aug 21, 2023
4e0bd99
IGNITE-19950 Creation seems to be complete
nizhikov Aug 23, 2023
00f53af
Merge branch 'master' into IGNITE-19950
nizhikov Aug 23, 2023
7aeca21
IGNITE-19950 Tests improvements
nizhikov Aug 24, 2023
b87338d
IGNITE-19950 Tests improvements
nizhikov Aug 28, 2023
2b81128
IGNITE-19950 Tests improvements
nizhikov Aug 28, 2023
a64d1cf
IGNITE-19950 Tests improvements
nizhikov Aug 28, 2023
a343bd1
IGNITE-19950 Tests improvements
nizhikov Aug 28, 2023
0bb66b5
Merge branch 'master' into IGNITE-19950
nizhikov Aug 28, 2023
725837c
IGNITE-19950 Tests improvements
nizhikov Aug 29, 2023
0418b46
IGNITE-19950 Tests improvements
nizhikov Aug 30, 2023
289bc27
IGNITE-19950 Restore WIP
nizhikov Aug 30, 2023
10059d8
IGNITE-19950 Test improvements
nizhikov Aug 30, 2023
1a37683
IGNITE-19950 Revert unnecessary changes.
nizhikov Aug 30, 2023
f6049cc
IGNITE-19950 Per partition parallelism
nizhikov Aug 30, 2023
fa8d34c
IGNITE-19950 Per partition parallelism
nizhikov Aug 30, 2023
42dbe75
IGNITE-19950 Tests improvements.
nizhikov Aug 30, 2023
d48c179
Merge branch 'master' into IGNITE-19950
nizhikov Aug 31, 2023
f754d9b
IGNITE-19950 Code improvements
nizhikov Aug 31, 2023
22f709d
IGNITE-19950 Remove restore code.
nizhikov Aug 31, 2023
1386384
IGNITE-19950 CRC check added.
nizhikov Aug 31, 2023
4b4b513
IGNITE-19950 Code review fixes.
nizhikov Aug 31, 2023
e23358a
IGNITE-19950 Code review fixes.
nizhikov Aug 31, 2023
53afadf
IGNITE-19950 Code review fixes.
nizhikov Aug 31, 2023
b36ebe5
IGNITE-19950 Code review fixes.
nizhikov Aug 31, 2023
d32df43
IGNITE-19950 Test improvements
nizhikov Aug 31, 2023
ca593e3
IGNITE-19950 Test improvements
nizhikov Aug 31, 2023
2d56981
IGNITE-19950 Test improvements
nizhikov Aug 31, 2023
65aef64
ISE-2839 Check WIP
nizhikov Sep 1, 2023
1520fc5
IGNITE-19950 Code review fixes
nizhikov Sep 1, 2023
70a0a22
IGNITE-19950 Tests improvements
nizhikov Sep 1, 2023
884bfc8
IGNITE-19950 Tests improvements
nizhikov Sep 1, 2023
a0dceec
IGNITE-19950 Tests improvements
nizhikov Sep 1, 2023
56666f1
IGNITE-19950 Tests improvements
nizhikov Sep 1, 2023
4aca1e4
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 1, 2023
9a26086
ISE-2839 Check command implemented
nizhikov Sep 3, 2023
9a57c29
ISE-2839 Check command implemented
nizhikov Sep 3, 2023
37d1b35
ISE-2839 Check command implemented (concurrent test is flaky).
nizhikov Sep 4, 2023
18d5a91
ISE-2839 Check command implemented (concurrent test is flaky).
nizhikov Sep 4, 2023
43b9415
ISE-2839 Check command implemented
nizhikov Sep 4, 2023
e65f5dc
ISE-2839 Improvement of create dump process
nizhikov Sep 4, 2023
97a7bc5
ISE-2839 Improvement of create dump process
nizhikov Sep 5, 2023
8b4db20
ISE-2839 Code review fixes
nizhikov Sep 5, 2023
8ad229d
Merge branch 'master' into IGNITE-19950
nizhikov Sep 5, 2023
d733a81
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 5, 2023
f830bf5
IGNITE-19950 Code review fixes
nizhikov Sep 5, 2023
99f9b33
IGNITE-19950 Code review fixes
nizhikov Sep 5, 2023
68b998f
IGNITE-19950 Code review fixes
nizhikov Sep 5, 2023
c2bb0ae
IGNITE-19950 Code review fixes
nizhikov Sep 5, 2023
ed067e6
IGNITE-19950 Bug fix
nizhikov Sep 5, 2023
ac895e8
IGNITE-19950 Bug fix
nizhikov Sep 5, 2023
b32e681
IGNITE-19950 Use max seen version to distinguish
nizhikov Sep 7, 2023
dfb70fe
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 7, 2023
5d04b16
Revert "IGNITE-19950 Use max seen version to distinguish"
nizhikov Sep 7, 2023
d24e3f2
Merge branch 'master' into IGNITE-19950
nizhikov Sep 11, 2023
e8f11d3
IGNITE-19950 Code review fixes
nizhikov Sep 11, 2023
e68751f
IGNITE-19950 Code review fixes
nizhikov Sep 11, 2023
81e0368
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 11, 2023
573ed5e
ISE-2839 Code review fixes
nizhikov Sep 11, 2023
6eaf5da
ISE-2839 Code review fixes
nizhikov Sep 11, 2023
2019898
IGNITE-19950 Tests added
nizhikov Sep 11, 2023
1ff78e7
IGNITE-19950 Tests added
nizhikov Sep 11, 2023
65a9f42
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 12, 2023
522d28f
IGNITE-19950 Code review fixes
nizhikov Sep 12, 2023
0d1cf9a
IGNITE-19950 Code review fixes
nizhikov Sep 12, 2023
44c93cd
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 12, 2023
53aa06a
IGNITE-19950 Code review fixes
nizhikov Sep 12, 2023
d16122f
IGNITE-19950 Code review fixes
nizhikov Sep 12, 2023
af3a457
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 12, 2023
bfce6ce
Merge branch 'master' into IGNITE-19950
nizhikov Sep 15, 2023
b04326b
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 15, 2023
929b6b4
IGNITE-19950 Code review changes.
nizhikov Sep 18, 2023
d9025e1
IGNITE-19950 Code review changes.
nizhikov Sep 18, 2023
49a07b4
IGNITE-19950 Code review changes.
nizhikov Sep 18, 2023
6347932
IGNITE-19950 Tests fixes.
nizhikov Sep 18, 2023
c22205c
IGNITE-19950 Tests fixes.
nizhikov Sep 18, 2023
30b40a5
IGNITE-19950 Tests fixes.
nizhikov Sep 18, 2023
1285fff
IGNITE-19950 Tests fixes.
nizhikov Sep 19, 2023
b50b072
IGNITE-19950 Tests fixes.
nizhikov Sep 19, 2023
f749811
IGNITE-19950 Tests fixes.
nizhikov Sep 19, 2023
f4f1210
Merge branch 'IGNITE-19950' into ISE-2839
nizhikov Sep 20, 2023
bd55bc9
IGNITE-19950 Tests fixes.
nizhikov Sep 20, 2023
73f16c9
IGNITE-19950 Tests fixes.
nizhikov Sep 20, 2023
600ea13
IGNITE-19950 NPE fix
nizhikov Sep 20, 2023
a888ac7
Merge branch 'cache_dumps' into IGNITE-20429
nizhikov Sep 22, 2023
21fa270
IGNITE-20429 Dump check command implemented
nizhikov Sep 22, 2023
490a32a
IGNITE-20429 Fix concurrent bug
nizhikov Sep 22, 2023
00dd52b
IGNITE-20429 Dump check command
nizhikov Sep 22, 2023
adc7946
IGNITE-20472 Dump reader API
nizhikov Sep 22, 2023
1eb1dbd
IGNITE-20429 Use per thread buffer for serializers.
nizhikov Sep 22, 2023
b4de161
Merge branch 'cache_dumps' into IGNITE-20429
nizhikov Sep 25, 2023
9badcdb
IGNITE-20429 Code review fixes.
nizhikov Sep 25, 2023
0eac15a
IGNITE-20429 Code review fixes.
nizhikov Sep 25, 2023
0734630
IGNITE-20429 Code review fixes.
nizhikov Sep 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.apache.ignite.internal.processors.cache.GridCacheOperation;
import org.apache.ignite.internal.processors.cache.GridLocalConfigManager;
import org.apache.ignite.internal.processors.cache.StoredCacheData;
import org.apache.ignite.internal.processors.cache.verify.IdleVerifyUtility.VerifyPartitionContext;
import org.apache.ignite.internal.processors.cache.verify.PartitionHashRecordV2;
import org.apache.ignite.internal.processors.cache.verify.TransactionsHashRecord;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
Expand Down Expand Up @@ -374,15 +375,10 @@ else if (txRec.state() == TransactionState.ROLLED_BACK) {
e.getKey(),
false,
consId,
e.getValue().hash,
e.getValue().verHash,
null,
0,
null,
0,
0,
0,
0
new VerifyPartitionContext(e.getValue())
)
));

Expand Down Expand Up @@ -453,12 +449,12 @@ private Map<Integer, StoredCacheData> readTxCachesData() throws IgniteCheckedExc
}

/** Holder for calculated hashes. */
private static class HashHolder {
public static class HashHolder {
/** */
private int hash;
public int hash;

/** */
private int verHash;
public int verHash;

/** */
public void increment(int hash, int verHash) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@
import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStore;
import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage;
import org.apache.ignite.internal.processors.cache.persistence.snapshot.dump.Dump;
import org.apache.ignite.internal.processors.cache.persistence.snapshot.dump.DumpEntry;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
import org.apache.ignite.internal.processors.cache.verify.IdleVerifyUtility.VerifyPartitionContext;
import org.apache.ignite.internal.processors.cache.verify.PartitionHashRecordV2;
import org.apache.ignite.internal.processors.compress.CompressionProcessor;
import org.apache.ignite.internal.util.GridStringBuilder;
Expand Down Expand Up @@ -167,7 +170,7 @@ public SnapshotPartitionsVerifyHandler(GridCacheSharedContext<?, ?> cctx) {
return Collections.emptyMap();
}

return meta.dump() ? checkDumpFiles() : checkSnapshotFiles(opCtx, grpDirs, meta, partFiles, punchHoleEnabled);
return meta.dump() ? checkDumpFiles(opCtx, partFiles) : checkSnapshotFiles(opCtx, grpDirs, meta, partFiles, punchHoleEnabled);
}

/** */
Expand Down Expand Up @@ -309,8 +312,85 @@ private Map<PartitionKeyV2, PartitionHashRecordV2> checkSnapshotFiles(
}

/** */
private Map<PartitionKeyV2, PartitionHashRecordV2> checkDumpFiles() {
return Collections.emptyMap();
private Map<PartitionKeyV2, PartitionHashRecordV2> checkDumpFiles(
SnapshotHandlerContext opCtx,
Set<File> partFiles
) throws IgniteCheckedException {
GridKernalContext snpCtx = cctx.snapshotMgr().createStandaloneKernalContext(
cctx.kernalContext().compress(),
opCtx.snapshotDirectory(),
opCtx.metadata().folderName()
);

for (GridComponent comp : snpCtx)
comp.start();

try {
Dump dump = new Dump(snpCtx, opCtx.snapshotDirectory());

Collection<PartitionHashRecordV2> partitionHashRecordV2s = U.doInParallel(
cctx.snapshotMgr().snapshotExecutorService(),
partFiles,
part -> caclucateDumpedPartitionHash(dump, cacheGroupName(part.getParentFile()), partId(part.getName()))
);

return partitionHashRecordV2s.stream().collect(Collectors.toMap(PartitionHashRecordV2::partitionKey, r -> r));
}
catch (Throwable t) {
log.error("Error executing handler: ", t);

throw t;
}
finally {
for (GridComponent comp : snpCtx)
comp.stop(true);
}
}

/** */
private PartitionHashRecordV2 caclucateDumpedPartitionHash(Dump dump, String grpName, int part) {
if (skipHash()) {
return new PartitionHashRecordV2(
new PartitionKeyV2(CU.cacheId(grpName), part, grpName),
false,
cctx.localNode().consistentId(),
null,
0,
PartitionHashRecordV2.PartitionState.OWNING,
new VerifyPartitionContext()
);
}

try {
String node = cctx.kernalContext().pdsFolderResolver().resolveFolders().folderName();

try (Dump.DumpedPartitionIterator iter = dump.iterator(node, CU.cacheId(grpName), part)) {
long size = 0;

VerifyPartitionContext ctx = new VerifyPartitionContext();

while (iter.hasNext()) {
DumpEntry e = iter.next();

ctx.update(e.key(), e.value(), null, ctx);

size++;
}

return new PartitionHashRecordV2(
new PartitionKeyV2(CU.cacheId(grpName), part, grpName),
false,
cctx.localNode().consistentId(),
null,
size,
PartitionHashRecordV2.PartitionState.OWNING,
ctx
);
}
}
catch (Exception e) {
throw new IgniteException(e);
}
}

/** {@inheritDoc} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.CacheObject;
Expand Down Expand Up @@ -97,6 +100,14 @@ public class CreateDumpFutureTask extends AbstractCreateSnapshotFutureTask imple
/** Local node is primary for set of group partitions. */
private final Map<Integer, Set<Integer>> grpPrimaries = new ConcurrentHashMap<>();

/**
* Map shared across all instances of {@link PartitionDumpContext} and {@link DumpEntrySerializer}.
* We use per thread buffer because number of threads is fewer then number of partitions.
* Regular count of partitions is {@link RendezvousAffinityFunction#DFLT_PARTITION_COUNT}
* and thread is {@link IgniteConfiguration#DFLT_PUBLIC_THREAD_CNT} whic is significantly less.
*/
private final ConcurrentMap<Long, ByteBuffer> thLocBufs = new ConcurrentHashMap<>();

/**
* @param cctx Cache context.
* @param srcNodeId Node id which cause snapshot task creation.
Expand Down Expand Up @@ -298,7 +309,10 @@ private void prepare() throws IOException, IgniteCheckedException {
}

closeFut = CompletableFuture.runAsync(
() -> onDone(new SnapshotFutureTaskResult(taken, null), err0),
() -> {
thLocBufs.clear();
onDone(new SnapshotFutureTaskResult(taken, null), err0);
},
cctx.kernalContext().pools().getSystemExecutorService()
);
}
Expand Down Expand Up @@ -329,7 +343,7 @@ private void createDumpLock() throws IgniteCheckedException, IOException {
private PartitionDumpContext dumpContext(int grp, int part) {
return dumpCtxs.computeIfAbsent(
toLong(grp, part),
key -> new PartitionDumpContext(cctx.kernalContext().cache().cacheGroup(grp), part)
key -> new PartitionDumpContext(cctx.kernalContext().cache().cacheGroup(grp), part, thLocBufs)
);
}

Expand Down Expand Up @@ -359,7 +373,7 @@ private class PartitionDumpContext implements Closeable {
private final AffinityTopologyVersion topVer;

/** Partition serializer. */
DumpEntrySerializer serdes;
private final DumpEntrySerializer serdes;

/** If {@code true} context is closed. */
volatile boolean closed;
Expand All @@ -370,8 +384,9 @@ private class PartitionDumpContext implements Closeable {
/**
* @param gctx Group context.
* @param part Partition id.
* @param thLocBufs Thread local buffers.
*/
public PartitionDumpContext(CacheGroupContext gctx, int part) {
public PartitionDumpContext(CacheGroupContext gctx, int part, ConcurrentMap<Long, ByteBuffer> thLocBufs) {
assert gctx != null;

try {
Expand All @@ -381,7 +396,7 @@ public PartitionDumpContext(CacheGroupContext gctx, int part) {

startVer = grpPrimaries.get(gctx.groupId()).contains(part) ? gctx.shared().versions().last() : null;

serdes = new DumpEntrySerializer();
serdes = new DumpEntrySerializer(thLocBufs);
changed = new HashMap<>();

for (int cache : gctx.cacheIds())
Expand Down Expand Up @@ -416,28 +431,24 @@ public void writeChanged(
) {
String reasonToSkip = null;

if (closed) // Partition already saved in dump.
if (closed) // Quick exit. Partition already saved in dump.
reasonToSkip = "partition already saved";
else {
writers.getAndIncrement();

try {
if (startVer != null && ver.isGreater(startVer))
if (closed) // Partition already saved in dump.
reasonToSkip = "partition already saved";
else if (startVer != null && ver.isGreater(startVer))
reasonToSkip = "greater version";
else if (!changed.get(cache).add(key)) // Entry changed several time during dump.
reasonToSkip = "changed several times";
else if (val == null)
reasonToSkip = "newly created or already removed"; // Previous value is null. Entry created after dump start, skip.
else {
synchronized (this) {
if (closed) // Partition already saved in dump.
reasonToSkip = "partition already saved";
else {
write(cache, expireTime, key, val);

changedCnt.increment();
}
}
write(cache, expireTime, key, val);

changedCnt.increment();
}
}
finally {
Expand Down Expand Up @@ -478,11 +489,8 @@ public boolean writeForIterator(
written = false;
else if (changed.get(cache).contains(key))
written = false;
else {
synchronized (this) {
write(cache, expireTime, key, val);
}
}
else
write(cache, expireTime, key, val);

if (log.isTraceEnabled()) {
log.trace("Iterator [" +
Expand All @@ -498,20 +506,22 @@ else if (changed.get(cache).contains(key))

/** */
private void write(int cache, long expireTime, KeyCacheObject key, CacheObject val) {
try {
ByteBuffer buf = serdes.writeToBuffer(cache, expireTime, key, val, cctx.cacheObjectContext(cache));
synchronized (serdes) { // Prevent concurrent access to the dump file.
try {
ByteBuffer buf = serdes.writeToBuffer(cache, expireTime, key, val, cctx.cacheObjectContext(cache));

if (file.writeFully(buf) != buf.limit())
throw new IgniteException("Can't write row");
}
catch (IOException | IgniteCheckedException e) {
throw new IgniteException(e);
if (file.writeFully(buf) != buf.limit())
throw new IgniteException("Can't write row");
}
catch (IOException | IgniteCheckedException e) {
throw new IgniteException(e);
}
}
}

/** {@inheritDoc} */
@Override public void close() {
synchronized (this) {
synchronized (this) { // Prevent concurrent close invocation.
if (closed)
return;

Expand All @@ -524,8 +534,6 @@ private void write(int cache, long expireTime, KeyCacheObject key, CacheObject v
LockSupport.parkNanos(1_000_000);

U.closeQuiet(file);

serdes = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
Expand All @@ -29,9 +30,13 @@
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.StoredCacheData;
Expand Down Expand Up @@ -67,9 +72,18 @@ public class Dump {
private final GridKernalContext cctx;

/**
* Map shared across all instances of {@link DumpEntrySerializer}.
* We use per thread buffer because number of threads is fewer then number of partitions.
* Regular count of partitions is {@link RendezvousAffinityFunction#DFLT_PARTITION_COUNT}
* and thread is {@link IgniteConfiguration#DFLT_PUBLIC_THREAD_CNT} whic is significantly less.
*/
private final ConcurrentMap<Long, ByteBuffer> thLocBufs = new ConcurrentHashMap<>();

/**
* @param cctx Kernal context.
* @param dumpDir Dump directory.
*/
public Dump(GridKernalContext cctx, File dumpDir) throws IgniteCheckedException {
public Dump(GridKernalContext cctx, File dumpDir) {
this.cctx = cctx;
this.dumpDir = dumpDir;

Expand Down Expand Up @@ -162,7 +176,7 @@ DumpedPartitionIterator iterator(String node, int group, int part, boolean exclu
throw new RuntimeException(e);
}

DumpEntrySerializer serializer = new DumpEntrySerializer();
DumpEntrySerializer serializer = new DumpEntrySerializer(thLocBufs);

serializer.kernalContext(cctx);

Expand Down
Loading