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

program won't end (resolved) & java.nio.file.AccessDeniedException on Windows #156

Closed
dipucian opened this issue Oct 22, 2019 · 27 comments
Closed
Labels
bug Something isn't working production release Tasks required for production release

Comments

@dipucian
Copy link

the following program will print done, but won't end normally.

  def main(args: Array[String]): Unit = {
    val map1: Map[String, String, Nothing, ApiIO] = persistent.Map[String, String, Nothing, ApiIO]("map1").get
    map1.put("abc" -> "def", "123" -> "456").get
    map1.foreach(println)
      .materialize.get
    map1.close().get
    println("done")
  }
@simerplaha
Copy link
Owner

simerplaha commented Oct 22, 2019

Thanks for reporting this. The Scheduler's thread was set to be alive in the background.

Fixed now. I will do a release within the next hour or so.

@simerplaha
Copy link
Owner

Released with v0.10.7. Should be on maven central soon.

@dipucian
Copy link
Author

I've just tried v0.10.7, the program did end, thanks~

but if I run the program a second time (after the first time finished successfully), I got this:

11:41:29.855 ERROR Maps {swaydb.core.map.Maps $anonfun$removeLast$1} - Failed to delete map 'map1\0\0;. Adding it back to the queue.
java.nio.file.AccessDeniedException: map1\0\0\1.log
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
	at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
	at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
	at java.nio.file.Files.delete(Files.java:1126)
	at swaydb.core.io.file.Effect$.delete(Effect.scala:116)
	at swaydb.core.io.file.Effect$.deleteIfExists(Effect.scala:120)
	at swaydb.core.io.file.DBFile.$anonfun$delete$2(DBFile.scala:275)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.Option.getOrElse(Option.scala:189)
	at swaydb.core.io.file.DBFile.delete(DBFile.scala:275)
	at swaydb.core.map.PersistentMap.delete(PersistentMap.scala:312)
	at swaydb.core.map.Maps.$anonfun$removeLast$2(Maps.scala:444)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at swaydb.IO$.apply(IO.scala:384)
	at swaydb.core.map.Maps.$anonfun$removeLast$1(Maps.scala:444)
	at scala.Option.map(Option.scala:230)
	at swaydb.core.map.Maps.removeLast(Maps.scala:443)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.pushForward(ThrottleCompaction.scala:195)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.pushForward(ThrottleCompaction.scala:168)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.pushForward(ThrottleCompaction.scala:148)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.runJob(ThrottleCompaction.scala:125)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.runJobs(ThrottleCompaction.scala:99)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.runNow(ThrottleCompaction.scala:69)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.run(ThrottleCompaction.scala:55)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.run(ThrottleCompaction.scala:44)
	at swaydb.core.level.compaction.throttle.ThrottleCompactor$.doWakeUp(ThrottleCompactor.scala:250)
	at swaydb.core.level.compaction.throttle.ThrottleCompactor$.wakeUp(ThrottleCompactor.scala:272)
	at swaydb.core.level.compaction.throttle.ThrottleCompactor$.wakeUp(ThrottleCompactor.scala:45)
	at swaydb.core.CoreInitializer$.$anonfun$sendInitialWakeUp$1(CoreInitializer.scala:187)
	at swaydb.core.CoreInitializer$.$anonfun$sendInitialWakeUp$1$adapted(CoreInitializer.scala:183)
	at swaydb.ActorWire.$anonfun$send$2(ActorWire.scala:143)
	at swaydb.ActorWire.$anonfun$send$2$adapted(ActorWire.scala:142)
	at swaydb.ActorWire.$anonfun$actor$2(ActorWire.scala:56)
	at swaydb.ActorWire.$anonfun$actor$2$adapted(ActorWire.scala:55)
	at swaydb.Actor.swaydb$Actor$$receive(Actor.scala:542)
	at swaydb.Actor.$anonfun$swaydb$Actor$$basicWakeUp$1(Actor.scala:453)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:659)
	at scala.util.Success.$anonfun$map$1(Try.scala:255)
	at scala.util.Success.map(Try.scala:213)
	at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
	at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
	at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
	at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
11:41:29.855 ERROR ThrottleCompaction$ {swaydb.core.level.compaction.throttle.ThrottleCompaction$ $anonfun$pushForward$2} - Failed to delete the oldest memory map 'map1\0\0'. The map is added back to the memory-maps queue.No more maps will be pushed to Level1 until this error is fixed as sequential conversion of memory-map files to Segments is required to maintain data accuracy. Please check file system permissions and ensure that SwayDB can delete files and reboot the database.
java.nio.file.AccessDeniedException: map1\0\0\1.log
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
	at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
	at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
	at java.nio.file.Files.delete(Files.java:1126)
	at swaydb.core.io.file.Effect$.delete(Effect.scala:116)
	at swaydb.core.io.file.Effect$.deleteIfExists(Effect.scala:120)
	at swaydb.core.io.file.DBFile.$anonfun$delete$2(DBFile.scala:275)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.Option.getOrElse(Option.scala:189)
	at swaydb.core.io.file.DBFile.delete(DBFile.scala:275)
	at swaydb.core.map.PersistentMap.delete(PersistentMap.scala:312)
	at swaydb.core.map.Maps.$anonfun$removeLast$2(Maps.scala:444)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at swaydb.IO$.apply(IO.scala:384)
	at swaydb.core.map.Maps.$anonfun$removeLast$1(Maps.scala:444)
	at scala.Option.map(Option.scala:230)
	at swaydb.core.map.Maps.removeLast(Maps.scala:443)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.pushForward(ThrottleCompaction.scala:195)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.pushForward(ThrottleCompaction.scala:168)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.pushForward(ThrottleCompaction.scala:148)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.runJob(ThrottleCompaction.scala:125)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.runJobs(ThrottleCompaction.scala:99)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.runNow(ThrottleCompaction.scala:69)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.run(ThrottleCompaction.scala:55)
	at swaydb.core.level.compaction.throttle.ThrottleCompaction$.run(ThrottleCompaction.scala:44)
	at swaydb.core.level.compaction.throttle.ThrottleCompactor$.doWakeUp(ThrottleCompactor.scala:250)
	at swaydb.core.level.compaction.throttle.ThrottleCompactor$.wakeUp(ThrottleCompactor.scala:272)
	at swaydb.core.level.compaction.throttle.ThrottleCompactor$.wakeUp(ThrottleCompactor.scala:45)
	at swaydb.core.CoreInitializer$.$anonfun$sendInitialWakeUp$1(CoreInitializer.scala:187)
	at swaydb.core.CoreInitializer$.$anonfun$sendInitialWakeUp$1$adapted(CoreInitializer.scala:183)
	at swaydb.ActorWire.$anonfun$send$2(ActorWire.scala:143)
	at swaydb.ActorWire.$anonfun$send$2$adapted(ActorWire.scala:142)
	at swaydb.ActorWire.$anonfun$actor$2(ActorWire.scala:56)
	at swaydb.ActorWire.$anonfun$actor$2$adapted(ActorWire.scala:55)
	at swaydb.Actor.swaydb$Actor$$receive(Actor.scala:542)
	at swaydb.Actor.$anonfun$swaydb$Actor$$basicWakeUp$1(Actor.scala:453)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:659)
	at scala.util.Success.$anonfun$map$1(Try.scala:255)
	at scala.util.Success.map(Try.scala:213)
	at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
	at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
	at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
	at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

@simerplaha
Copy link
Owner

simerplaha commented Oct 22, 2019

That error is basically saying that it failed to delete the log map1\0\0\1.log file it successfully compacted so its reverts back to it's previous stable state.

Reading this thread it looks like it's a Windows permission issue.

I don't have Windows. Could you please do me a favour and let me know if this program works for you? I wonder if you get the same java.nio.file.AccessDeniedException exception.

import java.nio.file.{Files, Paths}

object Test extends App {

  val folderPath = Paths.get("test")
  val filePath = folderPath.resolve("0.log")

  Files.createDirectories(folderPath)
  Files.createFile(filePath)

  assert(Files.exists(folderPath), "folder does not exists.")
  assert(Files.exists(filePath), "file does not exists.")

  Files.delete(filePath)
  Files.delete(folderPath)

  assert(Files.notExists(folderPath), "folder did not get deleted.")

}

@simerplaha
Copy link
Owner

@javadev do you also see the above java.nio.file.AccessDeniedException exception running dipucian's test app on Windows?

@dipucian
Copy link
Author

the program exit with 0, all asserts pass

@dipucian
Copy link
Author

dipucian commented Oct 22, 2019

I go to delete the map1 folder and re-run my test program, it run to completion as expected with 0.10.7.
but then I try to look into the newly generated map1 and found that there is no map1\0\0\1.log.
maybe this is why it fail with AccessDeniedException?

image

@simerplaha
Copy link
Owner

simerplaha commented Oct 22, 2019

The log file number increments each time you restart the database or if the file gets flushed due to it being full. So you will see 1.log files get created when you restart the database.

But you might be right about it trying to delete a file that doesn't exist. I will write some more random tests to see if I can somehow re-create the issue.

If by any chance if you still have the database folder (map1) and if there is no sensitive data in it then sending me the map1 folder zipped would help with debugging the AccessDeniedException on someones windows machine.

If not that's ok. Will eventually figure out the cause :)

@dipucian
Copy link
Author

the issue is 100% reproducible on my machine, the folder should contains no sensitive data as it should just be "abc" -> "def", "123" -> "456"

this is the folder after the first run:
map1.zip

and this is the folder after second run:
map1.zip

@simerplaha
Copy link
Owner

No problems running it on my Mac.

Just so that I can run it on the right machine what version of Windows, Java and Scala are you running?

@dipucian
Copy link
Author

java: jdk 1.8.0_152
scala: 2.13.1
Windows 7 Professional SP1

I could repeat the test on Windows 10 later today

@dipucian
Copy link
Author

full log if it helps~

notice that:

  1. it successfully open the DB, complete read/write before logging the exception
  2. the exception is caught and logged within your code, my program can still print done and exit normally afterwards

log.txt

@simerplaha
Copy link
Owner

Thank you so much. The full logs helps a lot.

Can you please do another debug run for me by adding "Thread.sleep(2000)" at the end of your code and see if you still see the exception?

def main(args: Array[String]): Unit = {
    val map1: Map[String, String, Nothing, ApiIO] = persistent.Map[String, String, Nothing, ApiIO]("map1").get
    map1.put("abc" -> "def", "123" -> "456").get
    map1.foreach(println)
      .materialize.get
    map1.close().get
    println("done")
    Thread.sleep(2000) //sleep for 2 seconds. after adding this here can you still see the exception?
  }

Another one by commenting out map1.close().get.

@dipucian
Copy link
Author

still see the exception, modified as:

  def main(args: Array[String]): Unit = {
    val map1: Map[String, String, Nothing, ApiIO] = persistent.Map[String, String, Nothing, ApiIO]("map1").get
    map1.put("abc" -> "def", "123" -> "456").get
    map1.foreach(println)
      .materialize.get
    println("sleep")
    Thread.sleep(2000)
    println("close")
    map1.close().get
    println("done")
  }

log.txt

same result commenting out close() or not, seems like the exception is thrown in a background thread and is not a direct result of calling close()

@simerplaha
Copy link
Owner

Yep the background compaction thread throws that exception. But the data is still safe as it's reverts back to previous stable state.

@javadev
Copy link
Contributor

javadev commented Oct 22, 2019

I found a discussion on SO.
https://stackoverflow.com/questions/28670576/getting-java-nio-file-accessdeniedexception-when-trying-to-write-to-a-folder

It looks like we are trying to delete file which was not closed.
We may check if file was closed, then close it and then delete. It may help.

Path destFile = Paths.get("dest file");
SeekableByteChannel destFileChannel = Files.newByteChannel(destFile);
//...
destFileChannel.close();  //removing this will throw java.nio.file.AccessDeniedException:
Files.copy(Paths.get("source file"), destFile);

@simerplaha
Copy link
Owner

The files are always closed before deleting.

@simerplaha simerplaha changed the title program won't end program won't end (resolved) & java.nio.file.AccessDeniedException on Windows Oct 24, 2019
@simerplaha simerplaha added good first issue Good for newcomers help wanted Extra attention is needed labels Oct 25, 2019
@simerplaha
Copy link
Owner

Labeled this as help wanted. It can be resolved fairly quickly with simple debugging but I'm unable to get access to a Windows machine any time soon. I'm available on Skype, Discord or Slack for voice or video call with anyone with Windows machine who can help get this issue resolved.

Suggestion on debugging

  • Run test program submitting by @dipucian above.
  • On second run debug DBFile.scala @ line 275 check if the fileCache's file is closed and why invoking Effect.deleteIfExists is causing AccessDeniedException on Windows.

@simerplaha simerplaha added the production release Tasks required for production release label Jan 26, 2020
@Ectras
Copy link

Ectras commented Aug 7, 2020

I also encountered this problem with Java, running on Windows 10.
My code just tries to retrieve data stored in an earlier program run:

Map<Integer, Integer, Void> map = MapConfig.functionsOff(Paths.get("data"), intSerializer(), intSerializer()).get();
System.out.println(map.get(42).get());

Strangely, this works for the first run after storing, but all subsequent program runs fail. This problem is severe, as the code is crashing already on the MapConfig get() call, which leads to termination of the whole program.

Exception in thread "main" java.nio.file.AccessDeniedException: data\0\1\2.log
	at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:89)
	at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
	at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
	at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:274)
	at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:105)
	at java.base/java.nio.file.Files.delete(Files.java:1146)
	at swaydb.core.io.file.Effect$.deleteIfExists(Effect.scala:122)
	at swaydb.core.io.file.DBFile.$anonfun$delete$2(DBFile.scala:272)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
	at scala.Option.getOrElse(Option.scala:201)
	at swaydb.core.io.file.DBFile.delete(DBFile.scala:272)
	at swaydb.core.map.PersistentMap.delete(PersistentMap.scala:330)
	at swaydb.core.map.Maps$.$anonfun$persistent$6(Maps.scala:117)
	at swaydb.IO$IterableIOImplicit.foreachIO(IO.scala:172)
	at swaydb.core.map.Maps$.$anonfun$persistent$2(Maps.scala:117)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.map.Maps$.persistent(Maps.scala:99)
	at swaydb.core.level.zero.LevelZero$.$anonfun$apply$3(LevelZero.scala:112)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.level.zero.LevelZero$.$anonfun$apply$1(LevelZero.scala:109)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.level.zero.LevelZero$.apply(LevelZero.scala:102)
	at swaydb.core.CoreInitializer$.$anonfun$apply$11(CoreInitializer.scala:326)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.CoreInitializer$.createLevels$1(CoreInitializer.scala:324)
	at swaydb.core.CoreInitializer$.$anonfun$apply$21(CoreInitializer.scala:387)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.CoreInitializer$.createLevels$1(CoreInitializer.scala:385)
	at swaydb.core.CoreInitializer$.$anonfun$apply$21(CoreInitializer.scala:387)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.CoreInitializer$.createLevels$1(CoreInitializer.scala:385)
	at swaydb.core.CoreInitializer$.$anonfun$apply$21(CoreInitializer.scala:387)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.CoreInitializer$.createLevels$1(CoreInitializer.scala:385)
	at swaydb.core.CoreInitializer$.$anonfun$apply$21(CoreInitializer.scala:387)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.CoreInitializer$.createLevels$1(CoreInitializer.scala:385)
	at swaydb.core.CoreInitializer$.$anonfun$apply$21(CoreInitializer.scala:387)
	at swaydb.IO$Right.flatMap(IO.scala:570)
	at swaydb.core.CoreInitializer$.createLevels$1(CoreInitializer.scala:385)
	at swaydb.core.CoreInitializer$.apply(CoreInitializer.scala:396)
	at swaydb.persistent.Map$.$anonfun$apply$1(Map.scala:114)
	at swaydb.Bag$$anon$22.suspend(Bag.scala:538)
	at swaydb.persistent.Map$.apply(Map.scala:77)
	at swaydb.java.persistent.MapConfig$Config.get(MapConfig.scala:241)
	at test.Test.main(Test.java:16)

Maybe I can help to fix it, but I have no experience with Scala.

@simerplaha
Copy link
Owner

@Ectras thank you for reporting this. I will try to replicate the error during this weekend. This issue is high priority now.

@simerplaha
Copy link
Owner

simerplaha commented Aug 8, 2020

Turns out on Windows we cannot delete memory-mapped files without clearing the in-memory MappedByteBuffer first. Currently MappedByteBuffer get cleared by a background Actor in a future and the file gets deleted immediately which is not allowed on Windows.

The following test passes on Mac but fails on Windows with the exception you reported.

Exception in thread "main" java.nio.file.AccessDeniedException: myFile.txt

import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class MemoryMapFileDeleteTest {

    public static void main(String[] args) throws IOException {
        Path path = Paths.get("myFile.txt");
        FileChannel channel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
        MappedByteBuffer buff = channel.map(FileChannel.MapMode.PRIVATE, 0, 4000);

        channel.close();
        buff.force();

        Files.delete(path); // throws java.nio.file.AccessDeniedException
    }
}

Solution is to delete memory-mapped files only after MappedByteBuffer is cleared. Will have this fix in the next release.

Temporarily you can create a database with memory-mapped disabled.

import swaydb.data.config.IOStrategy;
import swaydb.data.config.MMAP;
import swaydb.data.config.SegmentConfig;
import swaydb.java.persistent.MapConfig;

import java.nio.file.Paths;
import java.util.Collections;

import static swaydb.java.serializers.Default.intSerializer;

public class MMAPDisabledExample {

  public static void main(String[] args) {
    Map<Integer, Integer, Void> map =
      MapConfig
        .functionsOff(Paths.get("data"), intSerializer(), intSerializer())
        .setMmapAppendix(false)
        .setMmapMaps(false)
        .setSegmentConfig(defaultSegmentConfig())
        .get();

    map.put(42, 42);
    System.out.println(map.get(42).get());
  }

  /**
   * Builds a default SegmentConfig with MMAP disabled.
   * 
   * TODO - Make it easier to build SegmentConfig from default SegmentConfig
   * for overriding single fields like MMAP.
   */
  private static SegmentConfig defaultSegmentConfig() {
    return
      SegmentConfig
        .builder()
        .cacheSegmentBlocksOnCreate(true)
        .deleteSegmentsEventually(true)
        .pushForward(true)
        .mmap(MMAP.disabled()) //disable MMAP for Segment files.
        .minSegmentSize(StorageUnits.mb(2))
        .maxKeyValuesPerSegment(Integer.MAX_VALUE)
        .ioStrategy(
          ioStrategy -> {
            if (ioStrategy.isOpenResource() || ioStrategy.isReadDataOverview()) {
              return new IOStrategy.SynchronisedIO(true);
            } else {
              return new IOStrategy.SynchronisedIO(false);
            }
          }
        )
        .compression(info -> Collections.emptyList());
  }
}

@Ectras
Copy link

Ectras commented Aug 8, 2020

Thank you for your quick reply!
Indeed, I can confirm that my issue is solved using the workaround you provided.
Looking forward to the next release 👍

@simerplaha simerplaha removed the help wanted Extra attention is needed label Aug 8, 2020
@simerplaha
Copy link
Owner

simerplaha commented Aug 9, 2020

@Ectras just did a quick release of 0.14.4 with two important bug-fixes (unrelated to this issue which will be fixed in 0.14.5).

Disabling memory-mapped files from Java is also simpler now.

This following example is in Java.examples repo.

import swaydb.data.config.MMAP;
import swaydb.java.Map;
import swaydb.java.persistent.MapConfig;
import swaydb.persistent.DefaultConfigs;
import java.nio.file.Paths;
import static swaydb.java.serializers.Default.intSerializer;

public class MMAPDisabledExample {

  public static void main(String[] args) {
    Map<Integer, Integer, Void> map =
      MapConfig
        .functionsOff(Paths.get("data"), intSerializer(), intSerializer())
        .setMmapAppendix(false)
        .setMmapMaps(false)
        .setSegmentConfig(DefaultConfigs.segmentConfig(true).copyWithMmap(MMAP.disabled()))
        .get();

    map.put(42, 42);
    System.out.println(map.get(42).get());
  }
}

@simerplaha
Copy link
Owner

Just released v0.14.5 which resolves majority issues that were indirectly linked to this issue.

This issue is still in progress.

@simerplaha
Copy link
Owner

This issue is now fixed in 0.14.6 release. All test-cases are passing on Windows 10. Please try re-running your code.

@Ectras you can now run your code without disabling mmap config.

Map<Integer, Integer, Void> map =
  MapConfig
    .functionsOff(Paths.get("data"), intSerializer(), intSerializer())
    .get();

map.put(42, 42);
System.out.println(map.get(42).get());

As mentioned before, the problem was that Windows does not allow deleting memory-mapped files unless the file's in-memory MappedByteBuffer is cleared.

Solution

We now have a boolean cleanBeforeDelete flag in MMAP (memory-mapped) configuration. If cleanBeforeDelete is true then as the name says, memory-mapped files get cleaned before they are deleted which gets rid of the AccessDeniedException.

You don't really have to learn these configurations at all because the default instances are already pre-configured and I will document these configurations on the website in detail but here is a basic overview if you are interested.

deleteAfterClean

In the following snippets deleteAfterClean is enabled if the operating system is Windows.

val mmap =
  MMAP.Enabled(
    deleteAfterClean = OperatingSystem.isWindows, //deleteAfterClean if OS is Windows
    forceSave = ForceSave.Disabled //disable ForceSave
  )

Java code looks like

MMAP.Enabled enabled =
  MMAP.enabled(
    OperatingSystem.isWindows(),
    ForceSave.disabled()
  );

ForceSave

ForceSave is a new configuration (#251). On Windows calling force on MappedByteBuffer is slower in certain situations (specially when copying memory-mapped) so this configuration was required. We can also disable force if we just want high performance and do not care about write guarantees on fatal machine crashes.

What is force?

force is just a function on MappedByteBuffer that "Forces any changes made to this buffer's content to be written to the storage device containing the mapped file - JavaDoc".

We can configure force to tell SwayDB when it should be invoked on files in any of the following 3 cases or we can disable it.

//tells SwayDB to invoke forceSave before closing a file
val beforeClose =
  ForceSave.BeforeClose(
    enableBeforeCopy = true, //also enable before copying a file
    enableForReadOnlyMode = false, //do not force save if the file is in read-only mode
    logBenchmark = true //log out time take to execute force
  )

//tells SwayDB to invoke forceSave before cleaning a memory-mapped file's MappedByteBuffer
val beforeClean =
  ForceSave.BeforeClean(
    enableBeforeCopy = true,
    enableForReadOnlyMode = true,
    logBenchmark = true
  )

//tells SwayDB to invoke forceSave before copying a file. This occurs during compaction
//where a file can simply be copied into another level without merge when there are no overlapping keys. 
val beforeCopy =
  ForceSave.BeforeCopy(
    enableForReadOnlyMode = true,
    logBenchmark = true
  )

//disables force save. 
val disabled =
  ForceSave.Disabled

@simerplaha
Copy link
Owner

Added samples to example repos for setting the above MMAP and ForceSave configuration.

@simerplaha
Copy link
Owner

Closing this as it is resolved. Please reopen if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working production release Tasks required for production release
Projects
None yet
Development

No branches or pull requests

4 participants