├── src
├── SharpDB.Engine.Tests
│ ├── packages.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── DBTests.cs
│ └── SharpDB.Engine.Tests.csproj
├── SharpDB.Shared
│ ├── SharpDB.Shared.csproj
│ ├── MessageType.cs
│ └── Protocol.cs
├── SharpDB.Engine
│ ├── Cache
│ │ ├── ICacheProvider.cs
│ │ └── MemoryCacheProvider.cs
│ ├── DocumentLockedException.cs
│ ├── TransactionNotExistException.cs
│ ├── IO
│ │ ├── IDatabaseWriter.cs
│ │ ├── IDatabaseReader.cs
│ │ ├── DatabaseFileWriter.cs
│ │ └── DatabaseFileReader.cs
│ ├── Utils
│ │ └── ULongDescendingComparer.cs
│ ├── Domain
│ │ ├── DocumentRevision.cs
│ │ ├── Transaction.cs
│ │ ├── DocumentKey.cs
│ │ ├── DocumentStore.cs
│ │ └── Document.cs
│ ├── SharpDB.Engine.csproj
│ └── KeyValueDatabase.cs
├── SharpDB.Driver
│ ├── ISerializer.cs
│ ├── SharpDBException.cs
│ ├── SharpDB.Driver.csproj
│ ├── Binary
│ │ └── BinaryExtensions.cs
│ ├── SharpDBTransaction.cs
│ ├── SharpDBClient.cs
│ ├── Json
│ │ └── JsonExtensions.cs
│ ├── BsonSerializer.cs
│ └── SharpDBConnection.cs
├── SharpDB.Example
│ ├── SharpDB.Example.csproj
│ └── Program.cs
├── SharpDB.Engine.Specs
│ ├── App.config
│ ├── packages.config
│ ├── Hooks.cs
│ ├── Steps
│ │ ├── DatabaseGeneralSteps.cs
│ │ └── GetUpdateDeleteSteps.cs
│ ├── DatabaseContext.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Features
│ │ ├── GetUpdateDelete.feature
│ │ └── GetUpdateDelete.feature.cs
│ └── SharpDB.Engine.Specs.csproj
├── SharpDB.Server
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ └── FolderProfile.pubxml
│ ├── SharpDB.Server.csproj
│ ├── Program.cs
│ ├── ServerService.cs
│ └── Network
│ │ └── Server.cs
└── SharpDB.Server.sln
├── .gitignore
├── .github
└── ISSUE_TEMPLATE.md
├── CONTRIBUTING.md
├── LICENSE.md
└── README.md
/src/SharpDB.Engine.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/SharpDB.Shared/SharpDB.Shared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net40;netstandard2.0
4 |
5 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Cache/ICacheProvider.cs:
--------------------------------------------------------------------------------
1 | namespace SharpDB.Engine.Cache
2 | {
3 | public interface ICacheProvider
4 | {
5 | string Name { get; set; }
6 | void Set(long fileLocation, byte[] blob);
7 | byte[] Get(long fileLocation);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/SharpDB.Engine/DocumentLockedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Engine
7 | {
8 | public class DocumentLockedException : Exception
9 | {
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/TransactionNotExistException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Engine
7 | {
8 | public class TransactionNotExistException : Exception
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/SharpDB.Driver/ISerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SharpDB.Driver
4 | {
5 | public interface ISerializer : IDisposable
6 | {
7 | byte[] SerializeDocumentId(object documentId);
8 | byte[] SerializeDocument(T document);
9 | T DeserializeDocument(byte[] bytes);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/SharpDB.Driver/SharpDBException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Driver
7 | {
8 | public class SharpDBException : Exception
9 | {
10 | public SharpDBException(string message) : base(message)
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/SharpDB.Shared/MessageType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Shared
7 | {
8 | public enum MessageType : byte
9 | {
10 | Get, Update,Delete, StartTransaction, Commit, Rollback, TransactionGet, TransactionUpdate, TransactionDelete
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/SharpDB.Example/SharpDB.Example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/IO/IDatabaseWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using SharpDB.Engine.Domain;
3 |
4 | namespace SharpDB.Engine.IO
5 | {
6 | public interface IDatabaseWriter : IDisposable
7 | {
8 | string FileName { get; }
9 | long WriteDocument(DocumentId documentId, byte[] blob);
10 | void BeginTimestamp(ulong timestamp, int numberOfDocuments);
11 | void Flush();
12 | }
13 | }
--------------------------------------------------------------------------------
/src/SharpDB.Engine/IO/IDatabaseReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using SharpDB.Engine.Domain;
4 |
5 | namespace SharpDB.Engine.IO
6 | {
7 | public interface IDatabaseReader : IDisposable
8 | {
9 | string FileName { get; }
10 | Dictionary GetDocuments(out ulong dbTimestamp);
11 | byte[] ReadDocument(long fileLocation, int size);
12 | }
13 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | #ignore thumbnails created by windows
3 | Thumbs.db
4 | #Ignore files build by Visual Studio
5 | *.obj
6 | *.exe
7 | *.pdb
8 | *.user
9 | *.aps
10 | *.pch
11 | *.vspscc
12 | *_i.c
13 | *_p.c
14 | *.ncb
15 | *.suo
16 | *.tlb
17 | *.tlh
18 | *.bak
19 | *.cache
20 | *.ilk
21 | *.log
22 | *.orig
23 | [Bb]in
24 | [Dd]ebug*/
25 | *.lib
26 | *.sbr
27 | obj/
28 | [Rr]elease*/
29 | _ReSharper*/
30 | [Tt]est[Rr]esult*
31 | /src/.vs
32 | /src/packages
33 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Utils/ULongDescendingComparer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Engine.Utils
7 | {
8 | public class ULongDescendingComparer : IComparer
9 | {
10 | public int Compare(ulong x, ulong y)
11 | {
12 | if (x > y)
13 | {
14 | return -1;
15 | }
16 | else if (y > x)
17 | {
18 | return 1;
19 | }
20 | else
21 | {
22 | return 0;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/SharpDB.Server/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | Release
9 | netstandard2.0
10 | bin\Release\PublishOutput
11 |
12 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/Hooks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using SharpDB.Engine.Cache;
7 | using SharpDB.Engine.IO;
8 | using TechTalk.SpecFlow;
9 | using SharpDB.Engine;
10 |
11 | namespace SharpDB.Engine.Specs
12 | {
13 | public class Hooks
14 | {
15 | //[BeforeScenario]
16 | //public static void BeforeScenario()
17 | //{
18 |
19 | //}
20 |
21 | //[AfterScenario()]
22 | //public static void AfterScenario()
23 | //{
24 |
25 | //}
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/Steps/DatabaseGeneralSteps.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using TechTalk.SpecFlow;
6 |
7 | namespace SharpDB.Engine.Specs.Steps
8 | {
9 | [Binding]
10 | public class DatabaseGeneralSteps
11 | {
12 | private readonly DatabaseContext m_databaseContext;
13 |
14 | public DatabaseGeneralSteps(DatabaseContext databaseContext)
15 | {
16 | m_databaseContext = databaseContext;
17 | }
18 |
19 |
20 | [StepDefinition(@"Restrt the database")]
21 | public void WhenRestrtTheDatabase()
22 | {
23 | m_databaseContext.Restart();
24 | }
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Domain/DocumentRevision.cs:
--------------------------------------------------------------------------------
1 | namespace SharpDB.Engine.Domain
2 | {
3 | public class DocumentRevision
4 | {
5 | public DocumentRevision(DocumentId documentId, ulong timeStamp, long blobFileLocation, int blobSize)
6 | {
7 | DocumentId = documentId;
8 | TimeStamp = timeStamp;
9 | BlobFileLocation = blobFileLocation;
10 | BlobSize = blobSize;
11 |
12 | ExpireTimeStamp = 0;
13 | }
14 |
15 | public DocumentId DocumentId { get; private set; }
16 |
17 | public ulong TimeStamp { get; private set; }
18 |
19 | public ulong ExpireTimeStamp { get; private set; }
20 |
21 | public long BlobFileLocation { get; private set; }
22 |
23 | public int BlobSize { get; private set; }
24 |
25 | public bool IsDeleted {get { return BlobSize == 0; }}
26 |
27 | public void Expire(ulong timestamp)
28 | {
29 | ExpireTimeStamp = timestamp;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | If you are making a bug report, fill out the form below. If you are making a suggestion or something other than a bug report, then you may remove this boilerplate text.
2 |
3 | ### Describe your setup.
4 | Have you modified SharpDB in any way? What OS are you running on? What version of SharpDB and/or .NET Core/.NET Standard are you using?
5 |
6 | ### Describe the issue you are experiencing.
7 | Include details such as what you did, what happened, and what you think should have happened.
8 |
9 | ### Describe the steps to reproduce the problem.
10 | Here, you should tell us what we need to know in order to cause the same problem on our machines, so that we may debug it. This section is only needed if you are making a bug report.
11 |
12 | ### Anything else we need to know?
13 |
14 | If there is anything else that you think will help us solve the problem, please tell us here.
15 |
--------------------------------------------------------------------------------
/src/SharpDB.Server/SharpDB.Server.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 4.1.0.117-develop
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Domain/Transaction.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace SharpDB.Engine.Domain
4 | {
5 | public class Transaction
6 | {
7 | Dictionary m_updates = new Dictionary();
8 |
9 | public Transaction(int transactionId, ulong dbTimestamp)
10 | {
11 | TransactionId = transactionId;
12 | DBTimestamp = dbTimestamp;
13 | }
14 |
15 | public int TransactionId { get; private set; }
16 | public ulong DBTimestamp { get; private set; }
17 |
18 | public int Count
19 | {
20 | get { return m_updates.Count; }
21 | }
22 |
23 | public void AddUpdate(DocumentId documentId, byte[] blob)
24 | {
25 | m_updates.Add(documentId, blob);
26 | }
27 |
28 | public IEnumerable GetDocumentIds()
29 | {
30 | return m_updates.Keys;
31 | }
32 |
33 | public byte[] GetBlob(DocumentId documentId)
34 | {
35 | return m_updates[documentId];
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/SharpDB.Engine.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net40
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 4.5.0-preview2-26406-04
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 | First of all, thank you for wanting to contribute to SharpDB! As an open-source project, we encourage the community to pitch in and help wherever they can; bug reports, pull requests and more! However, we're just going to leave a few guidelines here to make sure everything goes smoothly.
3 |
4 | # Bug Reports & issues
5 | Encountered a bug? Well don't sit there and deal with it, report it! We have an issue template that should automatically place itself in the issue description box when you create an issue. Fill that out and hit "Submit Issue". We'll then evaluate the bug and _try_ and replicate it on our machines.
6 |
7 | BUT WAIT! Sometimes, it's not a bug, but maybe a feature request... In which case remove the issue template and describe whatever the issue's about.
8 |
9 | # Pull requests
10 | Making pull requests is even easier!
11 | 1. Fork the repository
12 | 2. Clone your fork
13 | 3. Make changes to your fork
14 | 4. Create a pull request!
15 | 5. We'll then evaluate your changes, and either merge it or request changes to be made.
16 |
--------------------------------------------------------------------------------
/src/SharpDB.Example/Program.cs:
--------------------------------------------------------------------------------
1 | using SharpDB.Driver;
2 | using System;
3 |
4 | namespace SharpDB.Example
5 | {
6 | internal class Program
7 | {
8 | private static void Main(string[] args)
9 | {
10 | using (SharpDBClient client = new SharpDBClient("tcp://127.0.0.1:5999"))
11 | {
12 | using (SharpDBConnection connection = client.GetConnection())
13 | {
14 | Account newAccount = new Account();
15 | newAccount.Name = "Hello";
16 | newAccount.Id = 1;
17 | connection.Update(newAccount);
18 | Account storedAccount = connection.Get(1);
19 | connection.DeleteDocument(newAccount);
20 | Console.WriteLine("Hello" == storedAccount.Name);
21 | }
22 | }
23 | }
24 |
25 | public class Account
26 | {
27 | public int Id { get; set; }
28 |
29 | public string Name { get; set; }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright © 2018 Ultz Limited & contributors
5 |
6 | Permission is hereby granted, free of charge, to any person
7 | obtaining a copy of this software and associated documentation
8 | files (the “Software”), to deal in the Software without
9 | restriction, including without limitation the rights to use,
10 | copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following
13 | conditions:
14 |
15 | The above copyright notice and this permission notice shall be
16 | included in all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/src/SharpDB.Shared/Protocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Shared
7 | {
8 | public static class Protocol
9 | {
10 | public static readonly byte[] Success = BitConverter.GetBytes(true);
11 | public static readonly byte[] Failed = BitConverter.GetBytes(false);
12 |
13 |
14 | public static readonly byte[] Get = new byte[] { (byte)MessageType.Get};
15 | public static readonly byte[] Update = new byte[] { (byte)MessageType.Update };
16 | public static readonly byte[] Delete = new byte[] { (byte)MessageType.Delete };
17 | public static readonly byte[] StartTransaction = new byte[] { (byte)MessageType.StartTransaction };
18 | public static readonly byte[] Commit = new byte[] { (byte)MessageType.Commit };
19 | public static readonly byte[] Rollback = new byte[] { (byte)MessageType.Rollback };
20 | public static readonly byte[] TransactionGet = new byte[] { (byte)MessageType.TransactionGet };
21 | public static readonly byte[] TransactionUpdate = new byte[] { (byte)MessageType.TransactionUpdate };
22 | public static readonly byte[] TransactionDelete = new byte[] { (byte)MessageType.TransactionDelete };
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/DatabaseContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using SharpDB.Engine.Cache;
7 | using SharpDB.Engine.IO;
8 |
9 | namespace SharpDB.Engine.Specs
10 | {
11 | public class DatabaseContext : IDisposable
12 | {
13 | public const string SpecsDbFile = "specs.dbfile";
14 |
15 | public DatabaseContext()
16 | {
17 | File.Delete(SpecsDbFile);
18 |
19 | Database = new KeyValueDatabase(filename => new DatabaseFileReader(filename), filename => new DatabaseFileWriter(filename),
20 | filename => new MemoryCacheProvider(filename));
21 | Database.FileName = SpecsDbFile;
22 | Database.Start();
23 | }
24 |
25 | public KeyValueDatabase Database { get; private set; }
26 |
27 | public void Restart()
28 | {
29 | Database.Stop();
30 | Database = new KeyValueDatabase(filename => new DatabaseFileReader(filename), filename => new DatabaseFileWriter(filename),
31 | filename => new MemoryCacheProvider(filename));
32 | Database.FileName = SpecsDbFile;
33 | Database.Start();
34 | }
35 |
36 | public void Dispose()
37 | {
38 | Database.Stop();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Cache/MemoryCacheProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.Caching;
5 | using System.Text;
6 |
7 | namespace SharpDB.Engine.Cache
8 | {
9 | public class MemoryCacheProvider : ICacheProvider
10 | {
11 | private MemoryCache m_memoryCache;
12 | private CacheItemPolicy m_policy;
13 |
14 | public MemoryCacheProvider(string name)
15 | {
16 | Name = name;
17 | m_memoryCache = new MemoryCache(Name);
18 |
19 | m_policy = new CacheItemPolicy();
20 | m_policy.SlidingExpiration = TimeSpan.FromHours(1);
21 | }
22 |
23 | public string Name { get; set; }
24 |
25 | public void Set(long fileLocation, byte[] blob)
26 | {
27 | string key = ConvertLongToString(fileLocation);
28 |
29 | m_memoryCache.Set(key, blob, m_policy);
30 | }
31 |
32 | private string ConvertLongToString(long fileLocation)
33 | {
34 | // converting the long to string
35 | byte[] bytes = BitConverter.GetBytes(fileLocation);
36 |
37 | return Encoding.ASCII.GetString(bytes);
38 | }
39 |
40 | public byte[] Get(long fileLocation)
41 | {
42 | string key = ConvertLongToString(fileLocation);
43 |
44 | return (byte[])m_memoryCache.Get(key);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/SharpDB.Server/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Topshelf;
3 |
4 | namespace SharpDB.Server
5 | {
6 | internal class Program
7 | {
8 | private static int Main(string[] args)
9 | {
10 | string databaseFile = "default";
11 | int port = 5999;
12 | return (int)HostFactory.Run(x =>
13 | {
14 | x.UseAssemblyInfoForServiceInfo();
15 |
16 | x.Service(settings => new ServerService(databaseFile, port), s =>
17 | {
18 | s.BeforeStartingService(_ => Console.WriteLine("BeforeStart"));
19 | s.BeforeStoppingService(_ => Console.WriteLine("BeforeStop"));
20 | });
21 |
22 | x.SetStartTimeout(TimeSpan.FromSeconds(10));
23 | x.SetStopTimeout(TimeSpan.FromSeconds(10));
24 |
25 | x.AddCommandLineDefinition("name", f => databaseFile = f);
26 | x.AddCommandLineDefinition("port", p => port = Convert.ToInt32(p));
27 |
28 | x.OnException((exception) =>
29 | {
30 | Console.WriteLine("Exception thrown - " + exception.Message);
31 | });
32 | });
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/SharpDB.Driver/SharpDB.Driver.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net40;netstandard2.0
5 | true
6 | https://github.com/Ultz-Limited/SharpDB/blob/master/LICENSE.md
7 | https://github.com/Ultz-Limited/SharpDB
8 |
9 | SharpDB is C# based High Performance Key Value NoSQL Database with ACID Transaction. SharpDB is using Multiversion concurrency control to achieve transactions. Because of using MVCC SharpDB is very fast in doing transaction (the downside is that SharpDB using a lof of space).
10 | For network protocl SharpDB is using NetMQ which is C# port of ZeroMQ, which means SharpDB is using very fast on the network as well.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/SharpDB.Driver/Binary/BinaryExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Newtonsoft.Json.Linq;
6 |
7 | namespace SharpDB.Driver.Binary
8 | {
9 | public static class BinaryExtensions
10 | {
11 | public static byte[] GetBinary(this SharpDBConnection connection, byte[] documentIdBytes)
12 | {
13 | return connection.GetInternal(documentIdBytes);
14 | }
15 |
16 | public static void UpdateBinary(this SharpDBConnection connection, byte[] documentIdBytes, byte[] blob)
17 | {
18 | connection.UpdateInternal(documentIdBytes, blob);
19 | }
20 |
21 | public static void DeleteBinary(this SharpDBConnection connection, byte[] documentIdBytes)
22 | {
23 | connection.DeleteInternal(documentIdBytes);
24 | }
25 |
26 | public static byte[] GetBinary(this SharpDBTransaction transaction, byte[] documentIdBytes)
27 | {
28 | return transaction.Connection.GetInternal(documentIdBytes, transaction);
29 | }
30 |
31 | public static void UpdateBinary(this SharpDBTransaction transaction, byte[] documentIdBytes, byte[] blob)
32 | {
33 | transaction.Connection.UpdateInternal(documentIdBytes, blob, transaction);
34 | }
35 |
36 | public static void DeleteBinary(this SharpDBTransaction transaction, byte[] documentIdBytes)
37 | {
38 | transaction.Connection.DeleteInternal(documentIdBytes, transaction);
39 | }
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SharpDB.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SharpDB.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("0cb03035-33c9-478f-a923-1140b9c72e8a")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SharpDB.Engine.Specs")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SharpDB.Engine.Specs")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("c50fd293-3157-4280-961e-cbb6bf5b0f4f")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/SharpDB.Driver/SharpDBTransaction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Driver
7 | {
8 | public class SharpDBTransaction : IDisposable
9 | {
10 | private bool m_transactionCompletionHandled = false;
11 |
12 | public SharpDBTransaction(SharpDBConnection connection, int transactionId)
13 | {
14 | Connection = connection;
15 |
16 | TransactionId = transactionId;
17 | TransactionIdBytes = BitConverter.GetBytes(transactionId);
18 | }
19 |
20 | internal byte[] TransactionIdBytes { get; private set; }
21 | internal int TransactionId { get; private set; }
22 | public SharpDBConnection Connection { get; private set; }
23 |
24 | public T Get(object documentId)
25 | {
26 | return Connection.TransactionGet(this, documentId);
27 | }
28 |
29 | public void Update(T document)
30 | {
31 | Connection.TransactionUpdate(this, document);
32 | }
33 |
34 | public void DeleteDocument(T document)
35 | {
36 | Connection.TransactionDeleteDocument(this, document);
37 | }
38 |
39 |
40 | public void DeleteDocumentById(object documentId)
41 | {
42 | Connection.TransactionDeleteDocumentById(this, documentId);
43 | }
44 |
45 | public void Commit()
46 | {
47 | m_transactionCompletionHandled = true;
48 | Connection.CommitTransaction(this);
49 | }
50 |
51 | public void Rollback()
52 | {
53 | m_transactionCompletionHandled = true;
54 | Connection.RollbackTransaction(this);
55 | }
56 |
57 | public void Dispose()
58 | {
59 | if (!m_transactionCompletionHandled)
60 | {
61 | Rollback();
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Domain/DocumentKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Engine.Domain
7 | {
8 | public class DocumentId
9 | {
10 | private int m_hash;
11 |
12 | public DocumentId(string id)
13 | : this(Encoding.Unicode.GetBytes(id))
14 | {
15 |
16 | }
17 |
18 | public DocumentId(int id)
19 | : this(BitConverter.GetBytes(id))
20 | {
21 |
22 | }
23 |
24 | public DocumentId(byte[] value)
25 | {
26 | Bytes = value;
27 | }
28 |
29 | public byte[] Bytes { get; private set; }
30 |
31 | public int Length
32 | {
33 | get { return Bytes.Length; }
34 | }
35 |
36 | public string GetBytesReprestnation()
37 | {
38 | StringBuilder bytesRepresentation = new StringBuilder();
39 |
40 | for (int i = 0; i < Bytes.Length - 1; i++)
41 | {
42 | bytesRepresentation.AppendFormat("{0},", (int)Bytes[i]);
43 | }
44 |
45 | bytesRepresentation.AppendFormat("{0}", (int) Bytes.Last());
46 |
47 | return bytesRepresentation.ToString();
48 | }
49 |
50 | protected bool Equals(DocumentId other)
51 | {
52 | if (other.Bytes.Length != Bytes.Length)
53 | {
54 | return false;
55 | }
56 |
57 | return Bytes.SequenceEqual(other.Bytes);
58 | }
59 |
60 | public override bool Equals(object obj)
61 | {
62 | if (ReferenceEquals(null, obj)) return false;
63 | if (ReferenceEquals(this, obj)) return true;
64 |
65 | if (obj.GetType() != this.GetType()) return false;
66 | return Equals((DocumentId)obj);
67 | }
68 |
69 | public override int GetHashCode()
70 | {
71 | if (m_hash == 0)
72 | {
73 | foreach (byte b in Bytes)
74 | {
75 | m_hash = 31 * m_hash + b;
76 | }
77 | }
78 | return m_hash;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Domain/DocumentStore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SharpDB.Engine.Domain
7 | {
8 | public class DocumentStore
9 | {
10 | Dictionary m_documents;
11 |
12 | public DocumentStore(Dictionary documents)
13 | {
14 | m_documents = documents;
15 | }
16 |
17 | public Document GetDocumentForUpdate(DocumentId documentId, int transactionId)
18 | {
19 | Document document;
20 |
21 | if (m_documents.TryGetValue(documentId, out document) && document.IsLocked && transactionId != document.TransactionId)
22 | {
23 | throw new DocumentLockedException();
24 | }
25 | else if (document != null && document.TransactionId == 0 && transactionId != -1)
26 | {
27 | document.TransactionLock(transactionId);
28 | }
29 |
30 | return document;
31 | }
32 |
33 | public Document GetDocument(DocumentId documentId)
34 | {
35 | Document document;
36 |
37 | m_documents.TryGetValue(documentId, out document);
38 |
39 | return document;
40 | }
41 |
42 | public void AddNewDocument(DocumentId documentId, ulong documentTimeStamp, long blobFileLocation, int blobSize)
43 | {
44 | m_documents.Add(documentId, new Document(documentId, documentTimeStamp, blobFileLocation, blobSize));
45 | }
46 |
47 | public void Cleanup(ulong timestamp)
48 | {
49 | foreach (KeyValuePair keyValuePair in m_documents)
50 | {
51 | keyValuePair.Value.Cleanup(timestamp);
52 | }
53 | }
54 |
55 | public IList GetAllDocumentsLatestRevision()
56 | {
57 | return m_documents.Values.Select(d => d.CurrentRevision).ToList();
58 | }
59 |
60 | public IList GetAllRevisionAboveTimestamp(ulong timestamp)
61 | {
62 | return m_documents.Values.SelectMany(d => d.GetRevisionsAboveTimestamp(timestamp)).ToList();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SharpDB
2 | =====
3 |
4 | SharpDB is C# based High Performance Key Value NoSQL Database with ACID Transaction.
5 | SharpDB is using [Multiversion concurrency control](http://en.wikipedia.org/wiki/Multiversion_concurrency_control) to achieve transactions.
6 | Because of using MVCC SharpDB is very fast in doing transaction (the downside is that SharpDB using a lof of space).
7 |
8 | For network protocl SharpDB is using [NetMQ](https://github.com/zeromq/netmq) which is C# port of ZeroMQ, which means SharpDB is using very fast on the network as well.
9 |
10 | ## Installation
11 |
12 | Packages of SharpDB is still not available, so download the code code and compile.
13 |
14 | After compiling run the following command to run the server:
15 |
16 | SharpDB.Server.exe run -name:test -port:5555
17 |
18 | You can change the name and port fields as you like. File called name.sdb will be created as the database file.
19 |
20 | You can also run the database as a service with the following commands (must run as administrator)
21 |
22 | SharpDB.Server.exe install -name:test -port:5555
23 | SharpDB.Server.exe start
24 |
25 | ## Using
26 |
27 | To use SharpDB from your application include the SharpDB.Driver assembly.
28 | Following is small example of using the SharpDB driver:
29 |
30 | [Test]
31 | public void UpdateGet()
32 | {
33 | using (SharpDBClient client = new SharpDBClient("tcp://127.0.0.1:5555"))
34 | {
35 | using (SharpDBConnection connection = client.GetConnection())
36 | {
37 | Account newAccount = new Account();
38 | newAccount.Name = "Hello";
39 | newAccount.Id = 1;
40 |
41 | connection.Update(newAccount);
42 |
43 | Account storedAccount = connection.Get(1);
44 |
45 | Assert.AreEqual("Hello", storedAccount.Name);
46 | }
47 | }
48 | }
49 |
50 | and the account class:
51 |
52 | public class Account
53 | {
54 | public int Id { get; set; }
55 |
56 | public string Name { get; set; }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/Features/GetUpdateDelete.feature:
--------------------------------------------------------------------------------
1 | Feature: GetUpdateDelete
2 | In order to manage my documents in the database
3 | As a developer
4 | I want to save, get or delete a document from the database
5 |
6 | Scenario: Saving a new document in the database
7 | When I update a document with Key=1 and Value="Doron"
8 | Then Document is in the database with Key=1 and Value="Doron"
9 |
10 | Scenario: Getting already stored document
11 | Given Document Key=1 and Value="Doron" in the DB
12 | When I get document with key=1
13 | Then Document should exist and Value="Doron"
14 |
15 | Scenario: Update already exist document
16 | Given Document Key=1 and Value="Doron" in the DB
17 | When I update a document with Key=1 and Value="Doron2"
18 | Then Document is in the database with Key=1 and Value="Doron2"
19 |
20 | Scenario: Delete already exist document
21 | Given Document Key=1 and Value="Doron" in the DB
22 | When I delete document with key=1
23 | Then Document with key=1 should not exist
24 |
25 | Scenario: Reupdate an object after got deleted
26 | Given Document Key=1 and Value="Doron" in the DB
27 | When I delete document with key=1
28 | And I update a document with Key=1 and Value="Doron2"
29 | Then Document is in the database with Key=1 and Value="Doron2"
30 |
31 | Scenario: Document is in database after restart
32 | When I update a document with Key=1 and Value="Doron"
33 | And Restrt the database
34 | Then Document is in the database with Key=1 and Value="Doron"
35 |
36 | Scenario: Deleting document already in the database before starting
37 | Given Document Key=1 and Value="Doron" in the DB
38 | And Restrt the database
39 | When I delete document with key=1
40 | And Restrt the database
41 | Then Document with key=1 should not exist
42 |
43 | Scenario: Update document already exist in the database before starting
44 | Given Document Key=1 and Value="Doron" in the DB
45 | And Restrt the database
46 | When I update a document with Key=1 and Value="Doron2"
47 | And Restrt the database
48 | Then Document is in the database with Key=1 and Value="Doron2"
49 |
--------------------------------------------------------------------------------
/src/SharpDB.Driver/SharpDBClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Concurrent;
4 | using System.Linq;
5 | using System.Text;
6 | using NetMQ;
7 |
8 | namespace SharpDB.Driver
9 | {
10 | public class SharpDBClient : IDisposable
11 | {
12 | private bool m_isDisposed = false;
13 |
14 | private ConcurrentBag m_connections = new ConcurrentBag();
15 |
16 | public SharpDBClient(string connectionString)
17 | {
18 | ConnectionString = connectionString;
19 | SerializerFactory = () => new BsonSerializer();
20 | }
21 |
22 | public Func SerializerFactory { get; set; }
23 |
24 | public string ConnectionString { get; private set; }
25 |
26 | public SharpDBConnection GetConnection()
27 | {
28 | NetMQSocket socket = new NetMQ.Sockets.RequestSocket();
29 | // socket.Options.CopyMessages = false;
30 | socket.Options.Linger = TimeSpan.FromSeconds(5);
31 | socket.Connect(ConnectionString);
32 |
33 | var connection = new SharpDBConnection(this, socket, SerializerFactory());
34 | m_connections.Add(connection);
35 |
36 | return connection;
37 | }
38 |
39 | internal void ReleaseConnection(SharpDBConnection connection)
40 | {
41 | connection.Socket.Dispose();
42 | connection.Serializer.Dispose();
43 | }
44 |
45 | public void Dispose()
46 | {
47 | lock (this)
48 | {
49 | if (!m_isDisposed)
50 | {
51 | SharpDBConnection connection;
52 | while (m_connections.TryTake(out connection))
53 | {
54 | connection.Dispose();
55 | }
56 | m_connections = null;
57 | m_isDisposed = true;
58 | }
59 | }
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/src/SharpDB.Server/ServerService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Common.Logging;
7 | using Common.Logging.Simple;
8 | using NetMQ;
9 | using SharpDB.Engine;
10 | using SharpDB.Engine.Cache;
11 | using SharpDB.Engine.IO;
12 | using Topshelf;
13 |
14 | namespace SharpDB.Server
15 | {
16 | #if NET40
17 |
18 | public class ServerService
19 | #else
20 |
21 | public class ServerService : ServiceControl
22 | #endif
23 | {
24 | private readonly string m_name;
25 | private readonly int m_port;
26 | private KeyValueDatabase m_db;
27 | private Network.Server m_server;
28 | private ILog m_log;
29 | private Task m_task;
30 |
31 | public ServerService(string name, int port)
32 | {
33 | m_name = name;
34 | m_port = port;
35 | }
36 |
37 | #if NET40
38 |
39 | public bool Start()
40 | #else
41 |
42 | public bool Start(HostControl hostControl)
43 | #endif
44 | {
45 | m_log = LogManager.GetLogger(this.GetType());
46 |
47 | m_log.InfoFormat("Starting SharpDB...");
48 | m_log.InfoFormat("Database Name: {0}", m_name);
49 |
50 | m_db = new KeyValueDatabase(filename => new DatabaseFileReader(filename), filename => new DatabaseFileWriter(filename),
51 | filename => new MemoryCacheProvider(filename));
52 | m_db.FileName = m_name + ".sdb";
53 | m_db.Start();
54 |
55 | m_server = new Network.Server(m_db, string.Format("tcp://*:{0}", m_port));
56 |
57 | m_task = Task.Factory.StartNew(m_server.Start);
58 | return true;
59 | }
60 |
61 | #if NET40
62 |
63 | public bool Stop()
64 | #else
65 |
66 | public bool Stop(HostControl hostControl)
67 | #endif
68 | {
69 | m_log.InfoFormat("Stopping SharpDB...");
70 |
71 | m_server.Stop();
72 | m_task.Wait();
73 |
74 | m_db.Stop();
75 |
76 | m_server = null;
77 | m_db = null;
78 | return true;
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/Steps/GetUpdateDeleteSteps.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using NUnit.Framework;
6 | using SharpDB.Engine.Cache;
7 | using SharpDB.Engine.Domain;
8 | using SharpDB.Engine.IO;
9 | using TechTalk.SpecFlow;
10 |
11 | namespace SharpDB.Engine.Specs.Steps
12 | {
13 | [Binding]
14 | public class GetUpdateDeleteSteps
15 | {
16 | private readonly DatabaseContext m_databaseContext;
17 |
18 | private byte[] m_documentValue;
19 |
20 | public GetUpdateDeleteSteps(DatabaseContext databaseContext)
21 | {
22 | m_databaseContext = databaseContext;
23 | }
24 |
25 | [Given(@"Document Key=(.*) and Value=""(.*)"" in the DB")]
26 | [When(@"I update a document with Key=(.*) and Value=""(.*)""")]
27 | public void UpdateDocument(int key, string value)
28 | {
29 | byte[] valueBytes = Encoding.ASCII.GetBytes(value);
30 |
31 | m_databaseContext.Database.Update(new DocumentId(key), valueBytes);
32 | }
33 |
34 | [Then(@"Document is in the database with Key=(.*) and Value=""(.*)""")]
35 | public void ThenDocumentIsInTheDatabaseWithKeyAndValue(int key, string value)
36 | {
37 | DocumentId documentId = new DocumentId(key);
38 |
39 | byte[] valueBytes = m_databaseContext.Database.Get(documentId);
40 |
41 | string dbValue = Encoding.ASCII.GetString(valueBytes);
42 |
43 | Assert.AreEqual(dbValue, value);
44 | }
45 |
46 | [When(@"I get document with key=(.*)")]
47 | public void WhenIGetDocumentWithKey(int key)
48 | {
49 | m_documentValue = m_databaseContext.Database.Get(new DocumentId(key));
50 | }
51 |
52 | [Then(@"Document should exist and Value=""(.*)""")]
53 | public void ThenDocumentShouldExistAndValue(string value)
54 | {
55 | Assert.Greater(value.Length, 0);
56 |
57 | string dbValue = Encoding.ASCII.GetString(m_documentValue);
58 |
59 | Assert.AreEqual(dbValue, value);
60 | }
61 |
62 | [When(@"I delete document with key=(.*)")]
63 | public void WhenIDeleteDocumentWithKey(int key)
64 | {
65 | m_databaseContext.Database.Delete(new DocumentId(Convert.ToInt32(key)));
66 | }
67 |
68 | [Then(@"Document with key=(.*) should not exist")]
69 | public void ThenDocumentWithKeyShouldNotExist(int key)
70 | {
71 | byte[] valueBytes = m_databaseContext.Database.Get(new DocumentId(key));
72 |
73 | Assert.AreEqual(0, valueBytes.Length);
74 | }
75 |
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/IO/DatabaseFileWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using SharpDB.Engine.Domain;
5 |
6 | namespace SharpDB.Engine.IO
7 | {
8 | public class DatabaseFileWriter : IDatabaseWriter
9 | {
10 | private FileStream m_writerStream;
11 |
12 | private byte[] m_writeDocumentBuffer;
13 |
14 | private byte[] m_writeStartTimestampBuffer;
15 |
16 | private const uint BlobMaxSize = 1024 * 1024; // One mega
17 |
18 | public DatabaseFileWriter(string fileName)
19 | {
20 | FileName = fileName;
21 |
22 | m_writerStream = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
23 | m_writerStream.Position = m_writerStream.Length;
24 |
25 | // calculating the max size of an object
26 | uint size = 2 + UInt16.MaxValue + 4 + BlobMaxSize;
27 |
28 | m_writeDocumentBuffer = new byte[size];
29 |
30 | // timestamp size + int16 size
31 | m_writeStartTimestampBuffer = new byte[12];
32 | }
33 |
34 | public string FileName { get; private set; }
35 |
36 | public long WriteDocument(DocumentId documentId, byte[] blob)
37 | {
38 | // calculate the size of the document including meta data
39 | int size = 2 + documentId.Length + 4 + blob.Length;
40 |
41 | int position = 0;
42 |
43 | Buffer.BlockCopy(BitConverter.GetBytes((UInt16)documentId.Length), 0, m_writeDocumentBuffer, position, 2);
44 | position += 2;
45 |
46 | Buffer.BlockCopy(documentId.Bytes, 0, m_writeDocumentBuffer, position, documentId.Length);
47 | position += documentId.Length;
48 |
49 | Buffer.BlockCopy(BitConverter.GetBytes(blob.Length), 0, m_writeDocumentBuffer, position, 4);
50 | position += 4;
51 |
52 | if (blob.Length > 0)
53 | {
54 | Buffer.BlockCopy(blob, 0, m_writeDocumentBuffer, position, blob.Length);
55 | }
56 |
57 | m_writerStream.Write(m_writeDocumentBuffer, 0, size);
58 |
59 | return m_writerStream.Position - blob.Length;
60 | }
61 |
62 | public void BeginTimestamp(ulong timestamp, int numberOfDocuments)
63 | {
64 | Buffer.BlockCopy(BitConverter.GetBytes(timestamp), 0, m_writeStartTimestampBuffer, 0, 8);
65 | Buffer.BlockCopy(BitConverter.GetBytes(numberOfDocuments), 0, m_writeStartTimestampBuffer, 8, 4);
66 |
67 | m_writerStream.Write(m_writeStartTimestampBuffer, 0, 12);
68 | }
69 |
70 | public void Flush()
71 | {
72 | m_writerStream.Flush();
73 | }
74 |
75 |
76 | public void Dispose()
77 | {
78 | m_writerStream.Dispose();
79 |
80 | m_writerStream = null;
81 | m_writeDocumentBuffer = null;
82 | m_writeStartTimestampBuffer = null;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Tests/DBTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using NUnit.Framework;
3 | using SharpDB.Engine.Domain;
4 | using SharpDB.Engine.IO;
5 | using SharpDB.Engine.Cache;
6 |
7 | namespace SharpDB.Engine.Tests
8 | {
9 | [TestFixture]
10 | public class DBTests
11 | {
12 | private KeyValueDatabase m_db;
13 |
14 | [SetUp]
15 | public void SetUpTest()
16 | {
17 | m_db = new KeyValueDatabase(filename => new DatabaseFileReader(filename), filename=> new DatabaseFileWriter(filename),
18 | filename => new MemoryCacheProvider(filename));
19 |
20 | m_db.Start();
21 | }
22 |
23 | [TearDown]
24 | public void TearDownTest()
25 | {
26 | m_db.Stop();
27 | m_db = null;
28 |
29 | File.Delete("test.dbfile");
30 | }
31 |
32 | [Test]
33 | public void TryReadFutureDocument()
34 | {
35 | int transactionId = m_db.StartTransaction();
36 |
37 | DocumentId id1 = new DocumentId("1");
38 |
39 | // this object is stored out of transaction therefore should be visible for the transaction
40 | m_db.Update(id1, new byte[1] { 0 });
41 |
42 | byte[] blob = m_db.TransactionGet(transactionId, id1);
43 |
44 | Assert.IsNull(blob);
45 |
46 | m_db.CommitTransaction(transactionId);
47 | }
48 |
49 | [Test]
50 | public void ReadOldRevisionDuringTransaction()
51 | {
52 | DocumentId id1 = new DocumentId("1");
53 |
54 | m_db.Update(id1, new byte[1] { 0 });
55 |
56 | int transactionId = m_db.StartTransaction();
57 |
58 | m_db.Update(id1, new byte[1] { 1 });
59 |
60 | byte[] blob = m_db.TransactionGet(transactionId, id1);
61 |
62 | Assert.AreEqual(0, blob[0]);
63 |
64 | m_db.CommitTransaction(transactionId);
65 |
66 | transactionId = m_db.StartTransaction();
67 |
68 | blob = m_db.TransactionGet(transactionId, id1);
69 |
70 | Assert.AreEqual(1, blob[0]);
71 |
72 | m_db.CommitTransaction(transactionId);
73 | }
74 |
75 | [Test]
76 | public void ReadDocumentUpdatedByTransaction()
77 | {
78 | DocumentId id1 = new DocumentId("1");
79 |
80 | m_db.Update(id1, new byte[1] { 0 });
81 |
82 | int transactionId = m_db.StartTransaction();
83 |
84 | m_db.TransactionUpdate(transactionId, id1, new byte[1] { 1 });
85 |
86 | byte[] blob = m_db.TransactionGet(transactionId, id1);
87 | Assert.AreEqual(1, blob[0]);
88 |
89 | // reading without transaction should return uncommitted document
90 | blob = m_db.Get(id1);
91 | Assert.AreEqual(0, blob[0]);
92 |
93 | m_db.CommitTransaction(transactionId);
94 |
95 | // reading the object agian after the transaction committed making sure we are reading the new committed document
96 | blob = m_db.Get(id1);
97 | Assert.AreEqual(1, blob[0]);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/Domain/Document.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using SharpDB.Engine.Utils;
4 |
5 | namespace SharpDB.Engine.Domain
6 | {
7 | public class Document
8 | {
9 | private SortedList m_revisions =
10 | new SortedList(new ULongDescendingComparer());
11 |
12 | public Document(DocumentId documentId, ulong documentTimeStamp, long blobFileLocation, int blobSize)
13 | {
14 | DocumentId = documentId;
15 |
16 | CurrentRevision = new DocumentRevision(documentId,documentTimeStamp, blobFileLocation, blobSize);
17 | }
18 |
19 | public DocumentId DocumentId { get; private set; }
20 |
21 | public DocumentRevision CurrentRevision { get; private set; }
22 |
23 | public int TransactionId { get; private set; }
24 |
25 | public bool IsLocked {get { return TransactionId != 0; }}
26 |
27 | public void Update(ulong documentTimeStamp, long blobFileLocation, int blobSize, bool saveRevision)
28 | {
29 | CurrentRevision.Expire(documentTimeStamp);
30 |
31 | if (saveRevision)
32 | {
33 | m_revisions.Add(CurrentRevision.TimeStamp, CurrentRevision);
34 | }
35 |
36 | CurrentRevision = new DocumentRevision(DocumentId, documentTimeStamp, blobFileLocation, blobSize);
37 | }
38 |
39 | public void TransactionLock(int transactionId)
40 | {
41 | TransactionId = transactionId;
42 | }
43 |
44 | public void RollbackTransaction()
45 | {
46 | TransactionId = 0;
47 | }
48 |
49 | public void CommitTransaction(ulong documentTimeStamp, long blobFileLocation, int blobSize)
50 | {
51 | TransactionId = 0;
52 |
53 | Update(documentTimeStamp, blobFileLocation, blobSize, true);
54 | }
55 |
56 | public DocumentRevision GetDocumentRevisionByTimestamp(ulong timestamp)
57 | {
58 | if (timestamp >= CurrentRevision.TimeStamp)
59 | {
60 | return CurrentRevision;
61 | }
62 | else
63 | {
64 | foreach (KeyValuePair documentRevision in m_revisions)
65 | {
66 | if (timestamp >= documentRevision.Value.TimeStamp && timestamp < documentRevision.Value.ExpireTimeStamp)
67 | {
68 | return documentRevision.Value;
69 | }
70 | }
71 |
72 | return null;
73 | }
74 | }
75 |
76 | public List GetRevisionsAboveTimestamp(ulong timestamp)
77 | {
78 | var list = m_revisions.Where(r => r.Key > timestamp).Select(r=> r.Value).ToList();
79 |
80 | if (CurrentRevision.TimeStamp > timestamp)
81 | {
82 | list.Add(CurrentRevision);
83 | }
84 |
85 | return list;
86 | }
87 |
88 | public void Cleanup(ulong timestamp)
89 | {
90 | // find all the revisions with timestamp lower than the minimum timestamp
91 | ulong[] keys = m_revisions.Keys.Where(k => k < timestamp).ToArray();
92 |
93 | foreach (ulong key in keys)
94 | {
95 | m_revisions.Remove(key);
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/SharpDB.Driver/Json/JsonExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Newtonsoft.Json.Linq;
6 |
7 | namespace SharpDB.Driver.Json
8 | {
9 | public static class JsonExtensions
10 | {
11 | public static JObject GetJObject(this SharpDBConnection connection, object documentId)
12 | {
13 | byte[] documentIdBytes = connection.Serializer.SerializeDocumentId(documentId);
14 |
15 | BsonSerializer serializer = connection.Serializer as BsonSerializer;
16 |
17 | byte[] blob = connection.GetInternal(documentIdBytes);
18 |
19 | return serializer.DeserializeToJObject(blob);
20 | }
21 |
22 | public static void UpdateJObject(this SharpDBConnection connection, JObject jobject)
23 | {
24 | JValue idToken = (JValue) jobject["Id"];
25 |
26 | object documentId = idToken.Value;
27 |
28 | byte[] documentIdBytes = connection.Serializer.SerializeDocumentId(documentId);
29 |
30 | BsonSerializer serializer = connection.Serializer as BsonSerializer;
31 |
32 | byte[] blob = serializer.SerializeFronJObject(jobject);
33 |
34 | connection.UpdateInternal(documentIdBytes, blob);
35 | }
36 |
37 | public static void DeleteJObject(this SharpDBConnection connection, JObject jobject)
38 | {
39 | JValue idToken = (JValue)jobject["Id"];
40 |
41 | DeleteJObject(connection, idToken);
42 | }
43 |
44 | public static void DeleteJObject(this SharpDBConnection connection, JValue idToken)
45 | {
46 | object documentId = idToken.Value;
47 |
48 | byte[] documentIdBytes = connection.Serializer.SerializeDocumentId(documentId);
49 |
50 | connection.DeleteInternal(documentIdBytes);
51 | }
52 |
53 | public static JObject GetJObject(this SharpDBTransaction transaction, object documentId)
54 | {
55 | byte[] documentIdBytes = transaction.Connection.Serializer.SerializeDocumentId(documentId);
56 |
57 | BsonSerializer serializer = transaction.Connection.Serializer as BsonSerializer;
58 |
59 | byte[] blob = transaction.Connection.GetInternal(documentIdBytes, transaction);
60 |
61 | return serializer.DeserializeToJObject(blob);
62 | }
63 |
64 | public static void UpdateJObject(this SharpDBTransaction transaction, JObject jobject)
65 | {
66 | JValue idToken = (JValue)jobject["Id"];
67 |
68 | object documentId = idToken.Value;
69 |
70 | byte[] documentIdBytes = transaction.Connection.Serializer.SerializeDocumentId(documentId);
71 |
72 | BsonSerializer serializer = transaction.Connection.Serializer as BsonSerializer;
73 |
74 | byte[] blob = serializer.SerializeFronJObject(jobject);
75 |
76 | transaction.Connection.UpdateInternal(documentIdBytes, blob, transaction);
77 | }
78 |
79 | public static void DeleteJObject(this SharpDBTransaction transaction, JObject jobject)
80 | {
81 | JValue idToken = (JValue)jobject["Id"];
82 |
83 | DeleteJObject(transaction, idToken);
84 | }
85 |
86 | public static void DeleteJObject(this SharpDBTransaction transaction, JValue idToken)
87 | {
88 | object documentId = idToken.Value;
89 |
90 | byte[] documentIdBytes = transaction.Connection.Serializer.SerializeDocumentId(documentId);
91 |
92 | transaction.Connection.DeleteInternal(documentIdBytes, transaction);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/SharpDB.Server.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2037
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpDB.Server", "SharpDB.Server\SharpDB.Server.csproj", "{5A25A3D1-5B63-4B5F-A606-581F8A858BF6}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpDB.Shared", "SharpDB.Shared\SharpDB.Shared.csproj", "{C88C0D35-FF8C-46B0-87EE-A14AF9A2895D}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpDB.Engine", "SharpDB.Engine\SharpDB.Engine.csproj", "{45FD4B03-C653-47BC-8190-46D3EF934ADD}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpDB.Driver", "SharpDB.Driver\SharpDB.Driver.csproj", "{5C55E790-79A4-491F-801E-502ED19D0FBE}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpDB.Engine.Tests", "SharpDB.Engine.Tests\SharpDB.Engine.Tests.csproj", "{4AFC56FA-8F1E-414D-954C-C3A4604800A7}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpDB.Example", "SharpDB.Example\SharpDB.Example.csproj", "{473F1737-D746-4347-92F6-D0C76A7464CC}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {5A25A3D1-5B63-4B5F-A606-581F8A858BF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {5A25A3D1-5B63-4B5F-A606-581F8A858BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {5A25A3D1-5B63-4B5F-A606-581F8A858BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {5A25A3D1-5B63-4B5F-A606-581F8A858BF6}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {C88C0D35-FF8C-46B0-87EE-A14AF9A2895D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {C88C0D35-FF8C-46B0-87EE-A14AF9A2895D}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {C88C0D35-FF8C-46B0-87EE-A14AF9A2895D}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {C88C0D35-FF8C-46B0-87EE-A14AF9A2895D}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {45FD4B03-C653-47BC-8190-46D3EF934ADD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {45FD4B03-C653-47BC-8190-46D3EF934ADD}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {45FD4B03-C653-47BC-8190-46D3EF934ADD}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {45FD4B03-C653-47BC-8190-46D3EF934ADD}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {5C55E790-79A4-491F-801E-502ED19D0FBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {5C55E790-79A4-491F-801E-502ED19D0FBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {5C55E790-79A4-491F-801E-502ED19D0FBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {5C55E790-79A4-491F-801E-502ED19D0FBE}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {4AFC56FA-8F1E-414D-954C-C3A4604800A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {4AFC56FA-8F1E-414D-954C-C3A4604800A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {4AFC56FA-8F1E-414D-954C-C3A4604800A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {4AFC56FA-8F1E-414D-954C-C3A4604800A7}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {473F1737-D746-4347-92F6-D0C76A7464CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {473F1737-D746-4347-92F6-D0C76A7464CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {473F1737-D746-4347-92F6-D0C76A7464CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {473F1737-D746-4347-92F6-D0C76A7464CC}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {1AC48B4D-D8D3-4EAF-88F5-762197106064}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/IO/DatabaseFileReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using SharpDB.Engine.Domain;
6 |
7 | namespace SharpDB.Engine.IO
8 | {
9 | public class DatabaseFileReader : IDatabaseReader
10 | {
11 | private FileStream m_readStream;
12 |
13 | public DatabaseFileReader(string fileName)
14 | {
15 | FileName = fileName;
16 |
17 | m_readStream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
18 | }
19 |
20 | public string FileName { get; private set; }
21 |
22 | public Dictionary GetDocuments(out ulong dbTimestamp)
23 | {
24 | dbTimestamp = 0;
25 |
26 | Dictionary documents = new Dictionary();
27 |
28 | // initialize the buffers
29 | byte[] timestampBuffer = new byte[12];
30 | byte[] documentIdLengthBuffer = new byte[2];
31 | byte[] documentIdBuffer = new byte[UInt16.MaxValue];
32 |
33 | byte[] blobLengthBuffer = new byte[4];
34 |
35 | int numberOfDocuments = 0;
36 | int documentsCounter = 0;
37 |
38 | // now we read all the objects meta data (not loading any data yet)
39 | while (m_readStream.Position < m_readStream.Length)
40 | {
41 | if (numberOfDocuments == documentsCounter)
42 | {
43 | // now we are reading the object timestamp
44 | m_readStream.Read(timestampBuffer, 0, 12);
45 | dbTimestamp = BitConverter.ToUInt64(timestampBuffer, 0);
46 |
47 | numberOfDocuments = BitConverter.ToInt32(timestampBuffer, 8);
48 | documentsCounter = 0;
49 | }
50 |
51 | // first is the object id lenth
52 | m_readStream.Read(documentIdLengthBuffer, 0, 2);
53 | UInt16 objectIdLength = BitConverter.ToUInt16(documentIdLengthBuffer, 0);
54 |
55 | // read the objectId
56 | m_readStream.Read(documentIdBuffer, 0, objectIdLength);
57 |
58 | byte[] documentIdBytes = new byte[objectIdLength];
59 | Buffer.BlockCopy(documentIdBuffer, 0, documentIdBytes, 0, objectIdLength);
60 |
61 | DocumentId documentId = new DocumentId(documentIdBytes);
62 |
63 | // read the blob length
64 | m_readStream.Read(blobLengthBuffer, 0, 4);
65 | int blobLength = BitConverter.ToInt32(blobLengthBuffer, 0);
66 | long blobLocation = m_readStream.Position;
67 |
68 | // take the position of the file to the next document
69 | m_readStream.Position += blobLength;
70 |
71 | // check if the document not exist and not deleted (zero length is deleted
72 | if (!documents.ContainsKey(documentId) && blobLength > 0)
73 | {
74 | documents.Add(documentId, new Document(documentId, dbTimestamp, blobLocation, blobLength));
75 | }
76 | else
77 | {
78 | // if the document is deleted we just remove the document from the store
79 | if (blobLength == 0)
80 | {
81 | documents.Remove(documentId);
82 | }
83 | else
84 | {
85 | Document document = documents[documentId];
86 |
87 | document.Update(dbTimestamp, blobLocation, blobLength, false);
88 | }
89 | }
90 |
91 | documentsCounter++;
92 | }
93 |
94 | if (documentsCounter != numberOfDocuments)
95 | {
96 | // database is corrupted, need to recover
97 | throw new Exception("Database is corrupted");
98 | }
99 |
100 | return documents;
101 | }
102 |
103 | public byte[] ReadDocument(long fileLocation, int size)
104 | {
105 | m_readStream.Position = fileLocation;
106 |
107 | byte[] blob = new byte[size];
108 | m_readStream.Read(blob, 0, size);
109 |
110 | return blob;
111 | }
112 |
113 |
114 | public void Dispose()
115 | {
116 | m_readStream.Dispose();
117 |
118 | m_readStream = null;
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Tests/SharpDB.Engine.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | 8.0.30703
8 | 2.0
9 | {4AFC56FA-8F1E-414D-954C-C3A4604800A7}
10 | Library
11 | Properties
12 | SharpDB.Engine.Tests
13 | SharpDB.Engine.Tests
14 | v4.0
15 | 512
16 |
17 |
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 | ..\packages\NUnit.3.10.1\lib\net40\nunit.framework.dll
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {45fd4b03-c653-47bc-8190-46d3ef934add}
58 | SharpDB.Engine
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
68 |
69 |
70 |
71 |
78 |
--------------------------------------------------------------------------------
/src/SharpDB.Driver/BsonSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Bson;
8 |
9 | namespace SharpDB.Driver
10 | {
11 | public class BsonSerializer : ISerializer
12 | {
13 | private JsonSerializer m_serializer;
14 | private MemoryStream m_memoryStream;
15 | private BsonWriter m_bsonWriter;
16 | private BinaryWriter m_binaryWriter;
17 |
18 | public const byte NumberType = 0;
19 | public const byte StringType = 1;
20 | public const byte CustomType = 2;
21 |
22 | public BsonSerializer()
23 | {
24 | m_serializer = new JsonSerializer();
25 | m_memoryStream = new MemoryStream(1024 * 1024);
26 | m_bsonWriter = new BsonWriter(m_memoryStream);
27 | m_binaryWriter = new BinaryWriter(m_memoryStream, Encoding.Unicode);
28 | }
29 |
30 | public virtual byte[] SerializeDocumentId(object documentId)
31 | {
32 | m_memoryStream.Position = 0;
33 |
34 | if (documentId is int)
35 | {
36 | // serializing the type of the document id in order to avoid case of 4 bytes string equal to int
37 | m_binaryWriter.Write(NumberType);
38 | m_binaryWriter.Write((int)documentId);
39 | }
40 | else if (documentId is long)
41 | {
42 | // serializing the type of the document id in order to avoid case of 8 bytes string equal to long
43 | m_binaryWriter.Write(NumberType);
44 |
45 | long number = (long) documentId;
46 |
47 | // to save space, if the number is less than the size of int we save int instead of long
48 | if (number < int.MaxValue)
49 | {
50 | m_binaryWriter.Write((int)number);
51 | }
52 | else
53 | {
54 | m_binaryWriter.Write(number);
55 | }
56 | }
57 | else if (documentId is string)
58 | {
59 | m_binaryWriter.Write(StringType);
60 | m_binaryWriter.Write((string)documentId);
61 | }
62 | else
63 | {
64 | m_binaryWriter.Write(CustomType);
65 | m_serializer.Serialize(m_bsonWriter, documentId);
66 | }
67 |
68 | byte[] buffer = new byte[m_memoryStream.Position];
69 | Buffer.BlockCopy(m_memoryStream.GetBuffer(), 0, buffer, 0, (int)m_memoryStream.Position);
70 |
71 | return buffer;
72 | }
73 |
74 | public virtual byte[] SerializeDocument(T document)
75 | {
76 | m_memoryStream.Position = 0;
77 | m_serializer.Serialize(m_bsonWriter, document);
78 |
79 | byte[] blob = new byte[m_memoryStream.Position];
80 | Buffer.BlockCopy(m_memoryStream.GetBuffer(), 0, blob, 0, (int)m_memoryStream.Position);
81 |
82 | return blob;
83 | }
84 |
85 | public virtual T DeserializeDocument(byte[] bytes)
86 | {
87 | using (var stream = new MemoryStream(bytes))
88 | {
89 | using (BsonReader reader = new BsonReader(stream))
90 | {
91 | return m_serializer.Deserialize(reader);
92 | }
93 | }
94 | }
95 |
96 | public virtual Newtonsoft.Json.Linq.JObject DeserializeToJObject(byte[] bytes)
97 | {
98 | using (var stream = new MemoryStream(bytes))
99 | {
100 | using (BsonReader reader = new BsonReader(stream))
101 | {
102 | return Newtonsoft.Json.Linq.JObject.Load(reader);
103 | }
104 | }
105 | }
106 |
107 | public virtual byte[] SerializeFronJObject(Newtonsoft.Json.Linq.JObject jobject)
108 | {
109 | m_memoryStream.Position = 0;
110 |
111 | jobject.WriteTo(m_bsonWriter);
112 |
113 | byte[] blob = new byte[m_memoryStream.Position];
114 | Buffer.BlockCopy(m_memoryStream.GetBuffer(), 0, blob, 0, (int)m_memoryStream.Position);
115 |
116 | return blob;
117 | }
118 |
119 | public virtual void Dispose()
120 | {
121 | m_memoryStream.Dispose();
122 |
123 | m_serializer = null;
124 | m_memoryStream = null;
125 | m_bsonWriter = null;
126 | m_binaryWriter = null;
127 | }
128 |
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/SharpDB.Engine.Specs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | 8.0.30703
8 | 2.0
9 | {93F474C3-4C19-4E87-8BA8-36EBDB4CBF06}
10 | Library
11 | Properties
12 | SharpDB.Engine.Specs
13 | SharpDB.Engine.Specs
14 | v4.0
15 | 512
16 |
17 |
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 | ..\packages\Newtonsoft.Json.11.0.2\lib\net40\Newtonsoft.Json.dll
39 |
40 |
41 | ..\packages\NUnit.3.10.1\lib\net40\nunit.framework.dll
42 |
43 |
44 |
45 |
46 | ..\packages\System.ValueTuple.4.5.0-preview2-26406-04\lib\portable-net40+sl4+win8+wp8\System.ValueTuple.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | True
58 | True
59 | GetUpdateDelete.feature
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | SpecFlowSingleFileGenerator
70 | GetUpdateDelete.feature.cs
71 |
72 |
73 |
74 |
75 |
76 | {A68DF91D-0DA4-453E-8CCE-9312FCA6B9F0}
77 | SharpDB.Engine
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
87 |
88 |
89 |
90 |
91 |
92 |
99 |
--------------------------------------------------------------------------------
/src/SharpDB.Server/Network/Server.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Common.Logging;
6 | using NetMQ;
7 | using SharpDB.Engine;
8 | using SharpDB.Engine.Domain;
9 | using SharpDB.Shared;
10 |
11 | namespace SharpDB.Server.Network
12 | {
13 | public class Server
14 | {
15 | private readonly KeyValueDatabase m_db;
16 | private readonly string[] m_addresses;
17 | private NetMQSocket m_serverSocket;
18 | private NetMQPoller m_poller;
19 | private ILog m_log;
20 |
21 | public Server(KeyValueDatabase db, params string[] addresses)
22 | {
23 | m_db = db;
24 | m_addresses = addresses;
25 | m_log = LogManager.GetLogger(this.GetType());
26 |
27 | if (!m_addresses.Any())
28 | {
29 | throw new ArgumentException("You must provide at least one address to listen too");
30 | }
31 | }
32 |
33 | public void Start()
34 | {
35 | using (m_serverSocket = new NetMQ.Sockets.ResponseSocket())
36 | {
37 | foreach (var address in m_addresses)
38 | {
39 | m_log.InfoFormat("Listening on {0}", address);
40 | m_serverSocket.Bind(address);
41 | }
42 | m_serverSocket.ReceiveReady += OnMessage;
43 |
44 | m_poller = new NetMQPoller();
45 | m_poller.Add(m_serverSocket);
46 |
47 | m_poller.Run();
48 | }
49 | }
50 |
51 | public void Stop()
52 | {
53 | m_poller.Stop();
54 | }
55 |
56 | private void OnMessage(object sender, NetMQSocketEventArgs e)
57 | {
58 | byte[] messageTypeBytes = m_serverSocket.ReceiveFrameBytes();
59 |
60 | MessageType messageType = (MessageType)messageTypeBytes[0];
61 |
62 | switch (messageType)
63 | {
64 | case MessageType.Get:
65 | Get();
66 | break;
67 |
68 | case MessageType.Update:
69 | Update();
70 | break;
71 |
72 | case MessageType.Delete:
73 | Delete();
74 | break;
75 |
76 | case MessageType.StartTransaction:
77 | StartTransaction();
78 | break;
79 |
80 | case MessageType.Commit:
81 | Commit();
82 | break;
83 |
84 | case MessageType.Rollback:
85 | Rollback();
86 | break;
87 |
88 | case MessageType.TransactionGet:
89 | TransactionGet();
90 | break;
91 |
92 | case MessageType.TransactionUpdate:
93 | TransactionUpdate();
94 | break;
95 |
96 | case MessageType.TransactionDelete:
97 | TransactionDelete();
98 | break;
99 |
100 | default:
101 | throw new ArgumentOutOfRangeException();
102 | }
103 | }
104 |
105 | private void TransactionDelete()
106 | {
107 | byte[] transactionIdBytes = m_serverSocket.ReceiveFrameBytes();
108 |
109 | int transactionId = BitConverter.ToInt32(transactionIdBytes, 0);
110 |
111 | byte[] documentIdBytes = m_serverSocket.ReceiveFrameBytes();
112 |
113 | DocumentId documentId = new DocumentId(documentIdBytes);
114 |
115 | try
116 | {
117 | m_db.TransactionDelete(transactionId, documentId);
118 |
119 | // sending success
120 | m_serverSocket.SendFrame(Protocol.Success);
121 | }
122 | catch (TransactionNotExistException ex)
123 | {
124 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Transaction doesn't exist");
125 | }
126 | catch (DocumentLockedException)
127 | {
128 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Document locked by another transaction");
129 | }
130 | }
131 |
132 | private void TransactionUpdate()
133 | {
134 | byte[] transactionIdBytes = m_serverSocket.ReceiveFrameBytes();
135 |
136 | int transactionId = BitConverter.ToInt32(transactionIdBytes, 0);
137 |
138 | byte[] documentIdBytes = m_serverSocket.ReceiveFrameBytes();
139 |
140 | DocumentId documentId = new DocumentId(documentIdBytes);
141 |
142 | byte[] blob = m_serverSocket.ReceiveFrameBytes();
143 |
144 | try
145 | {
146 | m_db.TransactionUpdate(transactionId, documentId, blob);
147 |
148 | // sending success
149 | m_serverSocket.SendFrame(Protocol.Success);
150 | }
151 | catch (TransactionNotExistException ex)
152 | {
153 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Transaction doesn't exist");
154 | }
155 | catch (DocumentLockedException)
156 | {
157 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Document locked by another transaction");
158 | }
159 | }
160 |
161 | private void TransactionGet()
162 | {
163 | byte[] transactionIdBytes = m_serverSocket.ReceiveFrameBytes();
164 |
165 | int transactionId = BitConverter.ToInt32(transactionIdBytes, 0);
166 |
167 | byte[] documentIdBytes = m_serverSocket.ReceiveFrameBytes();
168 |
169 | DocumentId documentId = new DocumentId(documentIdBytes);
170 |
171 | try
172 | {
173 | byte[] blob = m_db.TransactionGet(transactionId, documentId);
174 |
175 | if (blob == null)
176 | {
177 | blob = new byte[0];
178 | }
179 |
180 | m_serverSocket.SendMoreFrame(Protocol.Success).SendFrame(blob);
181 | }
182 | catch (TransactionNotExistException ex)
183 | {
184 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Transaction doesn't exist");
185 | }
186 | }
187 |
188 | private void Rollback()
189 | {
190 | byte[] transactionIdBytes = m_serverSocket.ReceiveFrameBytes();
191 |
192 | int transactionId = BitConverter.ToInt32(transactionIdBytes, 0);
193 |
194 | try
195 | {
196 | m_db.RollbackTransaction(transactionId);
197 | m_serverSocket.SendFrame(Protocol.Success);
198 | }
199 | catch (TransactionNotExistException ex)
200 | {
201 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Transaction doesn't exist");
202 | }
203 | }
204 |
205 | private void Commit()
206 | {
207 | byte[] transactionIdBytes = m_serverSocket.ReceiveFrameBytes();
208 |
209 | int transactionId = BitConverter.ToInt32(transactionIdBytes, 0);
210 |
211 | try
212 | {
213 | m_db.CommitTransaction(transactionId);
214 | m_serverSocket.SendFrame(Protocol.Success);
215 | }
216 | catch (TransactionNotExistException ex)
217 | {
218 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Transaction doesn't exist");
219 | }
220 | }
221 |
222 | private void StartTransaction()
223 | {
224 | int transactionId = m_db.StartTransaction();
225 |
226 | m_serverSocket.SendMoreFrame(Protocol.Success).SendFrame(BitConverter.GetBytes(transactionId));
227 | }
228 |
229 | private void Update()
230 | {
231 | byte[] documentIdBytes = m_serverSocket.ReceiveFrameBytes();
232 |
233 | DocumentId documentId = new DocumentId(documentIdBytes);
234 |
235 | byte[] blob = m_serverSocket.ReceiveFrameBytes();
236 |
237 | try
238 | {
239 | m_db.Update(documentId, blob);
240 |
241 | // sending success
242 | m_serverSocket.SendFrame(Protocol.Success);
243 | }
244 | catch (DocumentLockedException)
245 | {
246 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Document locked by another transaction");
247 | }
248 | }
249 |
250 | private void Delete()
251 | {
252 | byte[] documentIdBytes = m_serverSocket.ReceiveFrameBytes();
253 |
254 | DocumentId documentId = new DocumentId(documentIdBytes);
255 |
256 | try
257 | {
258 | m_db.Delete(documentId);
259 |
260 | // sending success
261 | m_serverSocket.SendFrame(Protocol.Success);
262 | }
263 | catch (DocumentLockedException)
264 | {
265 | m_serverSocket.SendMoreFrame(Protocol.Failed).SendFrame("Document locked by another transaction");
266 | }
267 | }
268 |
269 | private void Get()
270 | {
271 | byte[] documentIdBytes = m_serverSocket.ReceiveFrameBytes();
272 |
273 | DocumentId documentId = new DocumentId(documentIdBytes);
274 |
275 | byte[] blob = m_db.Get(documentId);
276 |
277 | if (blob == null)
278 | {
279 | blob = new byte[0];
280 | }
281 |
282 | m_serverSocket.SendMoreFrame(Protocol.Success).SendFrame(blob);
283 | }
284 | }
285 | }
--------------------------------------------------------------------------------
/src/SharpDB.Driver/SharpDBConnection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Reflection;
7 | using System.Reflection.Emit;
8 | using System.Text;
9 | using Common.Logging;
10 | using NetMQ;
11 | using Newtonsoft.Json;
12 | using Newtonsoft.Json.Bson;
13 | using SharpDB.Shared;
14 |
15 | namespace SharpDB.Driver
16 | {
17 | public class SharpDBConnection : IDisposable
18 | {
19 | private bool m_isDisposed;
20 | private SharpDBClient m_client;
21 | internal ILog Log { get; private set; }
22 |
23 | private IList m_transactions = new List();
24 |
25 | internal SharpDBConnection(SharpDBClient client, NetMQSocket socket, ISerializer serializer)
26 | {
27 | Log = LogManager.GetLogger(this.GetType());
28 | m_client = client;
29 | Socket = socket;
30 | Serializer = serializer;
31 | m_isDisposed = false;
32 | }
33 |
34 | internal NetMQSocket Socket { get; private set; }
35 | internal ISerializer Serializer { get; private set; }
36 |
37 | public void Dispose()
38 | {
39 | lock (this)
40 | {
41 | if (!m_isDisposed)
42 | {
43 | foreach (SharpDBTransaction transaction in m_transactions)
44 | {
45 | transaction.Rollback();
46 | }
47 |
48 | m_transactions.Clear();
49 |
50 | m_client.ReleaseConnection(this);
51 |
52 | Socket = null;
53 | Serializer = null;
54 | m_client = null;
55 |
56 | m_isDisposed = true;
57 | }
58 | }
59 | }
60 |
61 | private object GetDocumentId(T document)
62 | {
63 | ParameterExpression parameter = Expression.Parameter(typeof(object), "documentId");
64 |
65 | MethodInfo methodInfo = typeof(T).GetProperty("Id").GetGetMethod(); ;
66 |
67 | Expression> getIdExpression =
68 | Expression.Lambda>(
69 | Expression.Convert(Expression.Call(Expression.Convert(parameter, methodInfo.DeclaringType), methodInfo), typeof(object)), new[] { parameter });
70 |
71 | return getIdExpression.Compile()(document);
72 | }
73 |
74 | #region Internal binary methods
75 |
76 | internal byte[] GetInternal(byte[] documentIdBytes, SharpDBTransaction transaction = null)
77 | {
78 | if (transaction != null)
79 | {
80 | Socket.SendMoreFrame(Protocol.TransactionGet).SendMoreFrame(transaction.TransactionIdBytes);
81 | }
82 | else
83 | {
84 | Socket.SendMoreFrame(Protocol.Get);
85 | }
86 |
87 | Socket.SendFrame(documentIdBytes);
88 |
89 | bool result = BitConverter.ToBoolean(Socket.ReceiveFrameBytes(), 0);
90 |
91 | if (result)
92 | {
93 | return Socket.ReceiveFrameBytes();
94 | }
95 | else
96 | {
97 | string error = Socket.ReceiveFrameString();
98 |
99 | throw new SharpDBException(error);
100 | }
101 | }
102 |
103 | internal void UpdateInternal(byte[] documentIdBytes, byte[] blob, SharpDBTransaction transaction = null)
104 | {
105 | if (transaction != null)
106 | {
107 | Socket.SendMoreFrame(Protocol.TransactionUpdate).SendMoreFrame(transaction.TransactionIdBytes);
108 | }
109 | else
110 | {
111 | Socket.SendMoreFrame(Protocol.Update);
112 | }
113 |
114 | Socket.SendMoreFrame(documentIdBytes).SendFrame(blob);
115 |
116 | bool result = BitConverter.ToBoolean(Socket.ReceiveFrameBytes(), 0);
117 |
118 | if (!result)
119 | {
120 | string error = Socket.ReceiveFrameString();
121 |
122 | throw new SharpDBException(error);
123 | }
124 | }
125 |
126 | public void DeleteInternal(byte[] documentIdBytes, SharpDBTransaction transaction = null)
127 | {
128 | if (transaction != null)
129 | {
130 | Socket.SendMoreFrame(Protocol.TransactionDelete).SendMoreFrame(transaction.TransactionIdBytes);
131 | }
132 | else
133 | {
134 | Socket.SendMoreFrame(Protocol.Delete);
135 | }
136 |
137 | Socket.SendFrame(documentIdBytes);
138 |
139 | bool result = BitConverter.ToBoolean(Socket.ReceiveFrameBytes(), 0);
140 |
141 | if (!result)
142 | {
143 | string error = Socket.ReceiveFrameString();
144 |
145 | throw new SharpDBException(error);
146 | }
147 | }
148 |
149 | #endregion Internal binary methods
150 |
151 | #region Transactionless methods
152 |
153 | public T Get(object documentId)
154 | {
155 | byte[] documentIdBytes = Serializer.SerializeDocumentId(documentId);
156 |
157 | byte[] blob = GetInternal(documentIdBytes);
158 |
159 | if (blob.Length > 0)
160 | {
161 | return Serializer.DeserializeDocument(blob);
162 | }
163 | else
164 | {
165 | return default(T);
166 | }
167 | }
168 |
169 | public void Update(T document)
170 | {
171 | object documentId = GetDocumentId(document);
172 |
173 | byte[] documentIdBytes = Serializer.SerializeDocumentId(documentId);
174 |
175 | byte[] blob = Serializer.SerializeDocument(document);
176 |
177 | UpdateInternal(documentIdBytes, blob);
178 | }
179 |
180 | public void DeleteDocument(T document)
181 | {
182 | DeleteDocumentById(GetDocumentId(document));
183 | }
184 |
185 | public void DeleteDocumentById(object documentId)
186 | {
187 | byte[] documentIdBytes = Serializer.SerializeDocumentId(documentId);
188 |
189 | DeleteInternal(documentIdBytes);
190 | }
191 |
192 | #endregion Transactionless methods
193 |
194 | #region Transaction methods
195 |
196 | internal T TransactionGet(SharpDBTransaction transaction, object documentId)
197 | {
198 | byte[] documentIdBytes = Serializer.SerializeDocumentId(documentId);
199 |
200 | byte[] blob = GetInternal(documentIdBytes, transaction);
201 |
202 | if (blob.Length > 0)
203 | {
204 | return Serializer.DeserializeDocument(blob);
205 | }
206 | else
207 | {
208 | return default(T);
209 | }
210 | }
211 |
212 | internal void TransactionUpdate(SharpDBTransaction transaction, T document)
213 | {
214 | object documentId = GetDocumentId(document);
215 | byte[] documentIdBytes = Serializer.SerializeDocumentId(documentId);
216 |
217 | byte[] blob = Serializer.SerializeDocument(document);
218 |
219 | UpdateInternal(documentIdBytes, blob, transaction);
220 | }
221 |
222 | internal void TransactionDeleteDocument(SharpDBTransaction transaction, T document)
223 | {
224 | TransactionDeleteDocumentById(transaction, GetDocumentId(document));
225 | }
226 |
227 | internal void TransactionDeleteDocumentById(SharpDBTransaction transaction, object documentId)
228 | {
229 | byte[] documentIdBytes = Serializer.SerializeDocumentId(documentId);
230 |
231 | DeleteInternal(documentIdBytes, transaction);
232 | }
233 |
234 | public SharpDBTransaction StartTransaction()
235 | {
236 | Socket.SendMoreFrame(Protocol.StartTransaction);
237 |
238 | bool result = BitConverter.ToBoolean(Socket.ReceiveFrameBytes(), 0);
239 |
240 | if (result)
241 | {
242 | int transactionId = BitConverter.ToInt32(Socket.ReceiveFrameBytes(), 0);
243 |
244 | Log.DebugFormat("Transaction {0} started", transactionId);
245 |
246 | return new SharpDBTransaction(this, transactionId);
247 | }
248 | else
249 | {
250 | string error = Socket.ReceiveFrameString();
251 |
252 | Log.ErrorFormat("Failed to start transaction, error: {0}", error);
253 |
254 | throw new SharpDBException(Socket.ReceiveFrameString());
255 | }
256 | }
257 |
258 | internal void CommitTransaction(SharpDBTransaction transaction)
259 | {
260 | Socket.SendMoreFrame(Protocol.Commit).SendFrame(transaction.TransactionIdBytes);
261 |
262 | bool result = BitConverter.ToBoolean(Socket.ReceiveFrameBytes(), 0);
263 |
264 | m_transactions.Remove(transaction);
265 |
266 | if (!result)
267 | {
268 | string error = Socket.ReceiveFrameString();
269 |
270 | Log.ErrorFormat("Failed to commit transaction {0}, error: {1}", transaction.TransactionId, error);
271 |
272 | throw new SharpDBException(error);
273 | }
274 | else
275 | {
276 | Log.DebugFormat("Transaction {0} committed", transaction.TransactionId);
277 | }
278 | }
279 |
280 | internal void RollbackTransaction(SharpDBTransaction transaction)
281 | {
282 | Socket.SendMoreFrame(Protocol.Rollback).SendFrame(transaction.TransactionIdBytes);
283 |
284 | bool result = BitConverter.ToBoolean(Socket.ReceiveFrameBytes(), 0);
285 | m_transactions.Remove(transaction);
286 |
287 | if (!result)
288 | {
289 | string error = Socket.ReceiveFrameString();
290 |
291 | Log.ErrorFormat("Failed to rollback transaction {0}, error: {1}", transaction.TransactionId, error);
292 |
293 | throw new SharpDBException(error);
294 | }
295 | else
296 | {
297 | Log.DebugFormat("Transaction {0} rollbacked", transaction.TransactionId);
298 | }
299 | }
300 |
301 | #endregion Transaction methods
302 | }
303 | }
--------------------------------------------------------------------------------
/src/SharpDB.Engine.Specs/Features/GetUpdateDelete.feature.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This code was generated by SpecFlow (http://www.specflow.org/).
4 | // SpecFlow Version:1.9.0.77
5 | // SpecFlow Generator Version:1.9.0.0
6 | // Runtime Version:4.0.30319.18033
7 | //
8 | // Changes to this file may cause incorrect behavior and will be lost if
9 | // the code is regenerated.
10 | //
11 | // ------------------------------------------------------------------------------
12 | #region Designer generated code
13 | #pragma warning disable
14 | namespace SharpDB.Engine.Specs.Features
15 | {
16 | using TechTalk.SpecFlow;
17 |
18 |
19 | [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
20 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
21 | [NUnit.Framework.TestFixtureAttribute()]
22 | [NUnit.Framework.DescriptionAttribute("GetUpdateDelete")]
23 | public partial class GetUpdateDeleteFeature
24 | {
25 |
26 | private static TechTalk.SpecFlow.ITestRunner testRunner;
27 |
28 | #line 1 "GetUpdateDelete.feature"
29 | #line hidden
30 |
31 | [NUnit.Framework.TestFixtureSetUpAttribute()]
32 | public virtual void FeatureSetup()
33 | {
34 | testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
35 | TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "GetUpdateDelete", "In order to manage my documents in the database\r\nAs a developer\r\nI want to save, " +
36 | "get or delete a document from the database", ProgrammingLanguage.CSharp, ((string[])(null)));
37 | testRunner.OnFeatureStart(featureInfo);
38 | }
39 |
40 | [NUnit.Framework.TestFixtureTearDownAttribute()]
41 | public virtual void FeatureTearDown()
42 | {
43 | testRunner.OnFeatureEnd();
44 | testRunner = null;
45 | }
46 |
47 | [NUnit.Framework.SetUpAttribute()]
48 | public virtual void TestInitialize()
49 | {
50 | }
51 |
52 | [NUnit.Framework.TearDownAttribute()]
53 | public virtual void ScenarioTearDown()
54 | {
55 | testRunner.OnScenarioEnd();
56 | }
57 |
58 | public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
59 | {
60 | testRunner.OnScenarioStart(scenarioInfo);
61 | }
62 |
63 | public virtual void ScenarioCleanup()
64 | {
65 | testRunner.CollectScenarioErrors();
66 | }
67 |
68 | [NUnit.Framework.TestAttribute()]
69 | [NUnit.Framework.DescriptionAttribute("Saving a new document in the database")]
70 | public virtual void SavingANewDocumentInTheDatabase()
71 | {
72 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Saving a new document in the database", ((string[])(null)));
73 | #line 6
74 | this.ScenarioSetup(scenarioInfo);
75 | #line 7
76 | testRunner.When("I update a document with Key=1 and Value=\"Doron\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
77 | #line 8
78 | testRunner.Then("Document is in the database with Key=1 and Value=\"Doron\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
79 | #line hidden
80 | this.ScenarioCleanup();
81 | }
82 |
83 | [NUnit.Framework.TestAttribute()]
84 | [NUnit.Framework.DescriptionAttribute("Getting already stored document")]
85 | public virtual void GettingAlreadyStoredDocument()
86 | {
87 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Getting already stored document", ((string[])(null)));
88 | #line 10
89 | this.ScenarioSetup(scenarioInfo);
90 | #line 11
91 | testRunner.Given("Document Key=1 and Value=\"Doron\" in the DB", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
92 | #line 12
93 | testRunner.When("I get document with key=1", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
94 | #line 13
95 | testRunner.Then("Document should exist and Value=\"Doron\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
96 | #line hidden
97 | this.ScenarioCleanup();
98 | }
99 |
100 | [NUnit.Framework.TestAttribute()]
101 | [NUnit.Framework.DescriptionAttribute("Update already exist document")]
102 | public virtual void UpdateAlreadyExistDocument()
103 | {
104 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Update already exist document", ((string[])(null)));
105 | #line 15
106 | this.ScenarioSetup(scenarioInfo);
107 | #line 16
108 | testRunner.Given("Document Key=1 and Value=\"Doron\" in the DB", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
109 | #line 17
110 | testRunner.When("I update a document with Key=1 and Value=\"Doron2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
111 | #line 18
112 | testRunner.Then("Document is in the database with Key=1 and Value=\"Doron2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
113 | #line hidden
114 | this.ScenarioCleanup();
115 | }
116 |
117 | [NUnit.Framework.TestAttribute()]
118 | [NUnit.Framework.DescriptionAttribute("Delete already exist document")]
119 | public virtual void DeleteAlreadyExistDocument()
120 | {
121 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Delete already exist document", ((string[])(null)));
122 | #line 20
123 | this.ScenarioSetup(scenarioInfo);
124 | #line 21
125 | testRunner.Given("Document Key=1 and Value=\"Doron\" in the DB", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
126 | #line 22
127 | testRunner.When("I delete document with key=1", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
128 | #line 23
129 | testRunner.Then("Document with key=1 should not exist", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
130 | #line hidden
131 | this.ScenarioCleanup();
132 | }
133 |
134 | [NUnit.Framework.TestAttribute()]
135 | [NUnit.Framework.DescriptionAttribute("Reupdate an object after got deleted")]
136 | public virtual void ReupdateAnObjectAfterGotDeleted()
137 | {
138 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Reupdate an object after got deleted", ((string[])(null)));
139 | #line 25
140 | this.ScenarioSetup(scenarioInfo);
141 | #line 26
142 | testRunner.Given("Document Key=1 and Value=\"Doron\" in the DB", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
143 | #line 27
144 | testRunner.When("I delete document with key=1", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
145 | #line 28
146 | testRunner.And("I update a document with Key=1 and Value=\"Doron2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
147 | #line 29
148 | testRunner.Then("Document is in the database with Key=1 and Value=\"Doron2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
149 | #line hidden
150 | this.ScenarioCleanup();
151 | }
152 |
153 | [NUnit.Framework.TestAttribute()]
154 | [NUnit.Framework.DescriptionAttribute("Document is in database after restart")]
155 | public virtual void DocumentIsInDatabaseAfterRestart()
156 | {
157 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Document is in database after restart", ((string[])(null)));
158 | #line 31
159 | this.ScenarioSetup(scenarioInfo);
160 | #line 32
161 | testRunner.When("I update a document with Key=1 and Value=\"Doron\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
162 | #line 33
163 | testRunner.And("Restrt the database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
164 | #line 34
165 | testRunner.Then("Document is in the database with Key=1 and Value=\"Doron\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
166 | #line hidden
167 | this.ScenarioCleanup();
168 | }
169 |
170 | [NUnit.Framework.TestAttribute()]
171 | [NUnit.Framework.DescriptionAttribute("Deleting document already in the database before starting")]
172 | public virtual void DeletingDocumentAlreadyInTheDatabaseBeforeStarting()
173 | {
174 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Deleting document already in the database before starting", ((string[])(null)));
175 | #line 36
176 | this.ScenarioSetup(scenarioInfo);
177 | #line 37
178 | testRunner.Given("Document Key=1 and Value=\"Doron\" in the DB", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
179 | #line 38
180 | testRunner.And("Restrt the database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
181 | #line 39
182 | testRunner.When("I delete document with key=1", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
183 | #line 40
184 | testRunner.And("Restrt the database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
185 | #line 41
186 | testRunner.Then("Document with key=1 should not exist", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
187 | #line hidden
188 | this.ScenarioCleanup();
189 | }
190 |
191 | [NUnit.Framework.TestAttribute()]
192 | [NUnit.Framework.DescriptionAttribute("Update document already exist in the database before starting")]
193 | public virtual void UpdateDocumentAlreadyExistInTheDatabaseBeforeStarting()
194 | {
195 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Update document already exist in the database before starting", ((string[])(null)));
196 | #line 43
197 | this.ScenarioSetup(scenarioInfo);
198 | #line 44
199 | testRunner.Given("Document Key=1 and Value=\"Doron\" in the DB", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
200 | #line 45
201 | testRunner.And("Restrt the database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
202 | #line 46
203 | testRunner.When("I update a document with Key=1 and Value=\"Doron2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
204 | #line 47
205 | testRunner.And("Restrt the database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
206 | #line 48
207 | testRunner.Then("Document is in the database with Key=1 and Value=\"Doron2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
208 | #line hidden
209 | this.ScenarioCleanup();
210 | }
211 | }
212 | }
213 | #pragma warning restore
214 | #endregion
215 |
--------------------------------------------------------------------------------
/src/SharpDB.Engine/KeyValueDatabase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Common.Logging;
9 | using SharpDB.Engine.Domain;
10 | using SharpDB.Engine.IO;
11 | using SharpDB.Engine.Cache;
12 |
13 | namespace SharpDB.Engine
14 | {
15 | public class KeyValueDatabase
16 | {
17 | private readonly byte[] ZeroBlob = new byte[0];
18 |
19 | private readonly ILog m_log;
20 | private readonly Func m_readerFactory;
21 | private readonly Func m_writerFactory;
22 | private readonly Func m_cacheProviderFactory;
23 |
24 | private DocumentStore m_documentStore;
25 |
26 | private IDatabaseWriter m_databaseFileWriter;
27 | private IDatabaseReader m_databaseFileReader;
28 | private ICacheProvider m_cacheProvider;
29 |
30 | private int m_currentTransactionId = 1;
31 |
32 | private Dictionary m_pendingTransaction = new Dictionary();
33 |
34 | public KeyValueDatabase(Func readerFactory,
35 | Func writerFactory, Func cacheProviderFactory)
36 | {
37 | m_log = LogManager.GetLogger(this.GetType());
38 |
39 | FileName = "default.dbfile";
40 | m_readerFactory = readerFactory;
41 | m_writerFactory = writerFactory;
42 | m_cacheProviderFactory = cacheProviderFactory;
43 | }
44 |
45 | public ulong DBTimeStamp { get; private set; }
46 |
47 | public string FileName { get; set; }
48 |
49 | public void Start()
50 | {
51 | m_databaseFileWriter = m_writerFactory(FileName);
52 | m_databaseFileReader = m_readerFactory(FileName);
53 |
54 | m_cacheProvider = m_cacheProviderFactory(FileName);
55 |
56 | ulong timestamp;
57 |
58 | m_documentStore = new DocumentStore(m_databaseFileReader.GetDocuments(out timestamp));
59 |
60 | DBTimeStamp = timestamp;
61 | }
62 |
63 | public void Update(DocumentId documentId, byte[] blob)
64 | {
65 | Document document;
66 | try
67 | {
68 | document = m_documentStore.GetDocumentForUpdate(documentId, -1);
69 | }
70 | catch (DocumentLockedException)
71 | {
72 | m_log.InfoFormat("Update failed because document is locked by another transaction, documentId(bytes):{0}",
73 | documentId.GetBytesReprestnation());
74 |
75 | throw;
76 | }
77 |
78 | DBTimeStamp++;
79 |
80 | m_databaseFileWriter.BeginTimestamp(DBTimeStamp, 1);
81 |
82 | long documentLocation = m_databaseFileWriter.WriteDocument(documentId, blob);
83 |
84 | if (blob.Length > 0)
85 | {
86 | m_cacheProvider.Set(documentLocation, blob);
87 | }
88 |
89 | m_databaseFileWriter.Flush();
90 |
91 | if (document != null)
92 | {
93 | document.Update(DBTimeStamp, documentLocation, blob.Length, true);
94 | }
95 | else
96 | {
97 | m_documentStore.AddNewDocument(documentId, DBTimeStamp, documentLocation, blob.Length);
98 | }
99 | }
100 |
101 | public byte[] Get(DocumentId key)
102 | {
103 | Document document = m_documentStore.GetDocument(key);
104 |
105 | if (document != null)
106 | {
107 | return ReadInternal(document.CurrentRevision.BlobFileLocation, document.CurrentRevision.BlobSize);
108 | }
109 | else
110 | {
111 | return ZeroBlob;
112 | }
113 | }
114 |
115 | public void Delete(DocumentId documentId)
116 | {
117 | Update(documentId, ZeroBlob);
118 | }
119 |
120 | private byte[] ReadInternal(long fileLocation, int size)
121 | {
122 | if (size > 0)
123 | {
124 | byte[] blob = m_cacheProvider.Get(fileLocation);
125 |
126 | if (blob == null)
127 | {
128 | blob = m_databaseFileReader.ReadDocument(fileLocation, size);
129 | m_cacheProvider.Set(fileLocation, blob);
130 | }
131 |
132 | return blob;
133 | }
134 | else
135 | {
136 | return ZeroBlob;
137 | }
138 | }
139 |
140 | public int StartTransaction()
141 | {
142 | Transaction transaction = new Transaction(m_currentTransactionId, DBTimeStamp);
143 | m_currentTransactionId++;
144 |
145 | m_pendingTransaction.Add(transaction.TransactionId, transaction);
146 |
147 | m_log.DebugFormat("Start transaction {0}", transaction.TransactionId);
148 |
149 | return transaction.TransactionId;
150 | }
151 |
152 | public void TransactionUpdate(int transactionId, DocumentId documentId, byte[] blob)
153 | {
154 | Transaction transaction;
155 |
156 | if (!m_pendingTransaction.TryGetValue(transactionId, out transaction))
157 | {
158 | throw new TransactionNotExistException();
159 | }
160 |
161 | try
162 | {
163 | // mark the document is updated by transaction
164 | m_documentStore.GetDocumentForUpdate(documentId, transactionId);
165 | }
166 | catch (DocumentLockedException)
167 | {
168 | m_log.InfoFormat("Tranasction {1} update failed because document is locked by another transaction, documentId(bytes):{0}",
169 | documentId.GetBytesReprestnation(), transactionId);
170 |
171 | throw;
172 | }
173 |
174 | transaction.AddUpdate(documentId, blob);
175 | }
176 |
177 | public void TransactionDelete(int transactionId, DocumentId documentId)
178 | {
179 | TransactionUpdate(transactionId, documentId, ZeroBlob);
180 | }
181 |
182 | public byte[] TransactionGet(int transactionId, DocumentId documentId)
183 | {
184 | Transaction transaction;
185 |
186 | if (!m_pendingTransaction.TryGetValue(transactionId, out transaction))
187 | {
188 | throw new TransactionNotExistException();
189 | }
190 |
191 | Document document = m_documentStore.GetDocument(documentId);
192 | if (document == null)
193 | {
194 | return ZeroBlob;
195 | }
196 |
197 | // if updated by current transaction we just return the current blob
198 | if (document.TransactionId == transactionId)
199 | {
200 | return transaction.GetBlob(documentId);
201 | }
202 |
203 | // we need to find the current revision
204 | DocumentRevision revision = document.GetDocumentRevisionByTimestamp(transaction.DBTimestamp);
205 |
206 | // if there is no revision the object is not exist for this timestamp
207 | if (revision == null)
208 | {
209 | return ZeroBlob;
210 | }
211 |
212 | return ReadInternal(revision.BlobFileLocation, revision.BlobSize);
213 | }
214 |
215 | public void RollbackTransaction(int transactionId)
216 | {
217 | Transaction transaction;
218 |
219 | if (!m_pendingTransaction.TryGetValue(transactionId, out transaction))
220 | {
221 | throw new TransactionNotExistException();
222 | }
223 |
224 | foreach (DocumentId documentId in transaction.GetDocumentIds())
225 | {
226 | Document document = m_documentStore.GetDocument(documentId);
227 | document.RollbackTransaction();
228 | }
229 |
230 | m_pendingTransaction.Remove(transactionId);
231 |
232 | m_log.DebugFormat("Transaction {0} rollbacked", transaction.TransactionId);
233 | }
234 |
235 | public void CommitTransaction(int transactionId)
236 | {
237 | Transaction transaction;
238 |
239 | if (!m_pendingTransaction.TryGetValue(transactionId, out transaction))
240 | {
241 | throw new TransactionNotExistException();
242 | }
243 |
244 | if (transaction.Count > 0)
245 | {
246 | DBTimeStamp++;
247 |
248 | m_databaseFileWriter.BeginTimestamp(DBTimeStamp, transaction.Count);
249 |
250 | foreach (DocumentId documentId in transaction.GetDocumentIds())
251 | {
252 | byte[] blob = transaction.GetBlob(documentId);
253 |
254 | long documentLocation = m_databaseFileWriter.WriteDocument(documentId, blob);
255 |
256 | // we don't store deleted objects in the cache
257 | if (blob.Length > 0)
258 | {
259 | m_cacheProvider.Set(documentLocation, blob);
260 | }
261 |
262 | Document document = m_documentStore.GetDocument(documentId);
263 |
264 | if (document != null)
265 | {
266 | document.CommitTransaction(DBTimeStamp, documentLocation, blob.Length);
267 | }
268 | else
269 | {
270 | // only add the new document if the document is not deleted
271 | if (blob.Length > 0)
272 | {
273 | m_documentStore.AddNewDocument(documentId, DBTimeStamp, documentLocation, blob.Length);
274 | }
275 | }
276 | }
277 |
278 | m_databaseFileWriter.Flush();
279 | }
280 |
281 | m_pendingTransaction.Remove(transactionId);
282 |
283 | m_log.DebugFormat("Transaction {0} committed", transaction.TransactionId);
284 | }
285 |
286 | public void Cleanup()
287 | {
288 | ulong minTimestamp = DBTimeStamp;
289 |
290 | if (m_pendingTransaction.Any())
291 | {
292 | minTimestamp = m_pendingTransaction.Min(t => t.Value.DBTimestamp);
293 | }
294 |
295 | m_documentStore.Cleanup(minTimestamp);
296 | }
297 |
298 | public void Stop()
299 | {
300 | m_databaseFileWriter.Dispose();
301 | m_databaseFileWriter = null;
302 |
303 | m_databaseFileReader.Dispose();
304 | m_databaseFileReader = null;
305 |
306 | m_documentStore = null;
307 |
308 | m_pendingTransaction.Clear();
309 | m_pendingTransaction = null;
310 |
311 | m_currentTransactionId = 0;
312 | DBTimeStamp = 0;
313 | }
314 | }
315 | }
--------------------------------------------------------------------------------